[luoguSP10707] Count on a tree II

题意

原题链接
给定一棵树,节点 \(i\) 上有颜色 \(c_i\),多次询问,每次查询两点之间的路径中的不同颜色数。

sol

这是一道类似普通莫队 [luoguSP3267] D-query 的题目,但是是在树上询问的,因此考虑将树转化为序列计算。将树转化为序列包括 DFS 序,欧拉序和树链剖分三种,树链剖分复杂度更大,而 DFS 序无法解决本题,因此只能使用欧拉序。容易发现,每次询问点 \(x,y\) 会有两种情况(下设 \(rk1_i\) 表示欧拉序中第一次出现 \(i\) 的位置,\(rk2_i\) 表示欧拉序中第二次出现 \(i\) 的位置,且 \(rk1_x < rk1_y\)):

  1. \(\operatorname{lca}(x,y) = x\),此时路径即为从 \(x \to y\),也就是区间 \([rk1_x,rk1_y]\)
  2. \(\operatorname{lca}(x,y) \ne x\),此时路径为 \(x \to \operatorname{lca}(x,y) \to y\)。考虑到可能中间会遍历到其他的子树,因此这段路径对应区间 \([rk2_x,rk1_y]\),去除出现两次的点并加上 \(\operatorname{lca}(x, y)\)(因为 \(\operatorname{lca}(x,y)\) 一定会在 \(rk2_y\) 后出现)。

这样,就将树上问题转化为了序列上的问题,按照普通莫队的方法做即可。
有一个 trick,可以记录一个 \(st\) 数组表示遍历次数,如果第奇数次遍历则添加该点,否则删除该点,这样就将添加、删除、去双三个操作合并为了一个。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_map>
#include <cmath>using namespace std;const int N = 40005, M = 100005;int h[N], e[M], ne[M], idx;
int c[N];
int eular[N * 2], rk1[N], rk2[N], timestamp;
int fa[N][16], dep[N];
int block[N], ans[M], res;
unordered_map<int, int> buc;
bool st[N];
int n, m;struct Ask {int l, r, lca, id;bool operator< (const Ask &W) const {if (block[l] != block[W.l]) return block[l] < block[W.l];if (block[l] & 1) return r < W.r;else return r > W.r;}
} queries[M];void add(int a, int b){e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}void dfs_init(int u, int father){dep[u] = dep[father] + 1, fa[u][0] = father;eular[ ++ timestamp] = u, rk1[u] = timestamp;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (j == father) continue;dfs_init(j, u);}eular[ ++ timestamp] = u, rk2[u] = timestamp;
}int lca(int x, int y){if (dep[x] < dep[y]) swap(x, y);for (int i = 15; i >= 0; i -- ) {int fax = fa[x][i];if (dep[fax] >= dep[y]) x = fax;}if (x == y) return x;for (int i = 15; i >= 0; i -- ){int fax = fa[x][i], fay = fa[y][i];if (fax != fay) x = fax, y = fay;}return fa[x][0];
}void calc(int x){st[x] = !st[x];if (st[x]) {if (!buc[c[x]]) res ++ ;buc[c[x]] ++ ;} else {buc[c[x]] -- ;if (!buc[c[x]]) res -- ;}
}int main(){memset(h, -1, sizeof h);scanf("%d%d", &n, &m);for (int i = 1; i <= n; i ++ ) scanf("%d", &c[i]);for (int i = 1; i < n; i ++ ) {int a, b;scanf("%d%d", &a, &b);add(a, b), add(b, a);}dfs_init(1, 0);for (int k = 1; k <= 15; k ++ ) for (int i = 1; i <= n; i ++ )fa[i][k] = fa[fa[i][k - 1]][k - 1];for (int i = 1; i <= m; i ++ ){int x, y;scanf("%d%d", &x, &y);if (rk1[x] > rk1[y]) swap(x, y);int l = lca(x, y);if (x == l) queries[i] = {rk1[x], rk1[y], 0, i};else queries[i] = {rk2[x], rk1[y], l, i};}int Sz = sqrt(n * 2);int bcnt = ceil(2.0 * n / Sz);for (int i = 1; i <= bcnt; i ++ )for (int j = (i - 1) * Sz + 1; j <= min(i * Sz, n * 2); j ++ )block[j] = i; sort(queries + 1, queries + m + 1);int l = 1, r = 0;for (int i = 1; i <= m; i ++ ){Ask &q = queries[i];while (l > q.l) calc(eular[ -- l]);while (r < q.r) calc(eular[ ++ r]);while (l < q.l) calc(eular[l ++ ]);while (r > q.r) calc(eular[r -- ]);if (q.lca) calc(q.lca);ans[q.id] = res;if (q.lca) calc(q.lca);}for (int i = 1; i <= m; i ++ ) printf("%d\n", ans[i]);
}

蒟蒻犯的若至错误

  • 写莫队的时候 while 写成 if 了 awa。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/842403.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字孪生在智慧消防中能够发挥什么作用?

近年来,数字孪生技术在智慧城市的多个领域得到广泛应用,而在智慧消防中,其独特的优势更是为消防工作注入了强劲动力。通过数字孪生技术,消防管理可以从传统的事后应对转变为全面的事前预防、实时监测和精准指挥,为城市安全带来革命性的提升。 实现消防设施全景监控 数字孪…

SpringBoot 如何解析配置文件中的list?

1.情景展示在配置文件当中,我们是可以使用list来设置参数对应的参数值的(也就是:参数值可以是list)。 YML文件如上图所示,在配置文件当中(如:Yml) ,我们是可以直接使用list。 其格式就是: 下划线➕空格,后面跟数组元素即可,一行就代表一个元素。 properties文件 如…

一个不错的软件版本命名规范!

之前写了一篇如何自动生成版本号的文章, 《让你的C程序,自动打印版本信息》 初衷是让自己的程序在运行时自动打印与版本相关的信息, 避免测试时因为版本信息不确定导致的一些功能对应不上去的问题, 当时留了一个坑,写一篇关于如何设计一个相对规范的版本号的文章, 现在把…

css 三角形

.threes{position: relative;}.threes::before {position: absolute;top: -6rpx;right: -50rpx;content: "";display: inline-block;width: 0;height: 0;border-left: 10rpx solid transparent; /* 调整这个值以改变三角形的大小 */border-bottom: 10rpx solid #1780…

直播预约 | 数据驱动:直击离散制造业数智化转型实践

11月28日,KaiwuDB 携手施耐德电气全球供应链中国及中工互联联合发起《数据驱动:直击离散制造业数智化转型实践》主题直播,欢迎观看。11月28日,KaiwuDB 携手施耐德电气全球供应链中国及中工互联联合发起《数据驱动:直击离散制造业数智化转型实践》主题直播,针对离散制造业…

隔行/列设置背景色

1. 概述编辑1.1 问题描述 通过根据条件显示不同背景色可以实现满足某条件时,改变行/列的背景色,那么如果表格的行与行之间需要显示为不同的颜色,该如何实现呢? 1.2 解决思路 通过在「条件属性>背景」中使用公式 row() 获取行号(公式 col() 获取列号),再通过计算实现。…

使用命令打开SQLServer配置管理器

当安装完sqlserver数据库后 发现找不到菜单sqlserver配置管理器时使用命令(window+r)打开cmd端,根据sqlserver版本号选择对应命令SQLServerManager15.msc(对于 SQLServer2019) SQLServerManager14.msc(对于 SQLServer2017) SQLServerManager13.msc(对于 SQLServer2016) SQLServ…

c#配置文件的使用记录

我有一个控制台应用程序, 其中有一些变量我想写在配置文件中 原来的app.config是这样的位置放在configuration中程序的引用中添加 在程序中使用如下写法即可取得配置中的对应值 string userNo = ConfigurationManager.AppSettings["UserNo"];

数据库开发规范v1.0

一、建表规约【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是,0 表示否)。说明:任何字段如果为非负数,必须是 unsigned。 正例:表达逻辑删除的字段名 deleted_flag,0 表示删除,1 表示未删除。【强制】表名、字段名必…

ABAP开发规范V1.0

1. 概要 1.1目的 该文档定义了在开发与维护ABAP程序过程中必须遵守的规范与标准。该文档应当被视为一个动态的文档,该文档会根据需要进行增补和修订。 开发规范的重要作用在于保持整个开发团队的开发风格一致,提高程序质量,降低维护压力。 1.2适用范围 所有ABAP开发及系统配…

如何保证RocketMQ消息不丢失

如何保证RocketMQ消息不丢失 目录如何保证RocketMQ消息不丢失背景什么情况下RokectMQ消息会丢失解决RocketMQ消息丢失问题消息生产防止消息丢失Broker端消息丢失消费端处理消息总结 背景 在金融系统中MQ消息的消息丢失是不允许的,消息的丢失会导致支付状态订单状态出现混乱。接…

优化服务入口设立:提升用户满意度的全方位指南

一、引言 在数字化浪潮的席卷下,企业服务质量与服务入口设立的合理性、便捷性紧密相连。一个出色的服务入口能成为企业与用户之间的高效桥梁,不仅方便用户获取服务,还能提升企业运营效率,进而对用户满意度产生深远影响。然而,现实中不少企业从IT部门角度构建的服务流程和工…