题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4940

题解

对于换根操作,处理方法就很套路了。

首先先假定以 \(1\) 为根做一遍 dfs,那么在 \(rt\) 为根的时候,对于一个点 \(x\),如果 \(rt\) 不在 \(x\) 的以 \(1\) 为根时的子树中,那么 \(x\) 在 \(rt\) 为根时的子树和以 \(1\) 时的子树一样。

如果 \(rt\) 在 \(x\) 的以 \(1\) 为根时的子树中,那么我们求出 \(y\) 表示在以 \(1\) 为根时,\(x\) 的孩子中,子树里面有 \(rt\) 的。那么那么 \(x\) 在 \(rt\) 为根时的子树就是除去 \(y\) 在以 \(1\) 为根时的子树的全部部分。

如果 \(rt = x\),那么显然子树就是整棵树。

然后根据子树的 dfs 序的连续性,我们就可以把原题转化为这样的问题:

给定两个区间 \([l_1, r_1]\) 和 \([l_2, r_2]\),求出两个区间中有多少对点的点权一样。

有了前面一道题 [Snoi2017]一个简单的询问 的经验,我们知道,这个题目的做法是把一个有四个参数的询问拆分成四个有一个参数的询问。

这个的具体做法就参见我对这个题目的题解了。


#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;} typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii; namespace io {
const int SIZE = (1 << 21) + 1;
char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
// getchar
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
// print the remaining part
inline void flush () {
fwrite (obuf, 1, oS - obuf, stdout);
oS = obuf;
}
// putchar
inline void putc (char x) {
*oS ++ = x;
if (oS == oT) flush ();
}
// input a signed integer
template <class I>
inline void gi (I &x) {
for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;
}
// print a signed integer
template <class I>
inline void print (I &x) {
if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
while (x) qu[++ qr] = x % 10 + '0', x /= 10;
while (qr) putc (qu[qr --]);
}
}
#define read io::gi const int N = 100000 + 7;
const int M = 500000 + 7; #define bl(x) (((x) - 1) / blo + 1) int n, m, dfc, Q, ansi, blo;
ll val;
ll ans[M];
int a[N], b[N], cl[N], cr[N];
int dep[N], f[N], siz[N], son[N], top[N], dfn[N], pre[N]; struct Query {
int opt, l, r;
ll *ans;
inline Query() {}
inline Query(const int &opt, const int &l, const int &r, ll *ans) : opt(opt), l(l), r(r), ans(ans) {
if (l > r) std::swap(this->l, this->r);
assert(this->l <= this->r);
}
inline bool operator < (const Query &b) const { return bl(l) == bl(b.l) ? r < b.r : l < b.l; }
} q[M << 4]; struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); } inline void dfs1(int x, int fa = 0) {
f[x] = fa, dep[x] = dep[fa] + 1, siz[x] = 1;
for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
}
inline void dfs2(int x, int pa) {
top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
if (!son[x]) return; dfs2(son[x], pa);
for fec(i, x, y) if (y != son[x] && y != f[x]) dfs2(y, y);
}
inline int gson(int x, int p) {
int g = 0;
while (top[x] != top[p]) g = top[x], x = f[g];
return x == p ? g : son[p];
}
inline bool intr(int x, int p) { return dfn[x] >= dfn[p] && dfn[x] <= dfn[p] + siz[p] - 1; } inline void addq(int l1, int r1, int l2, int r2, ll *ans) {
q[++Q] = Query(1, r1, r2, ans);
if (l1 > 1) q[++Q] = Query(-1, l1 - 1, r2, ans);
if (l2 > 1) q[++Q] = Query(-1, l2 - 1, r1, ans);
if (l1 > 1 && l2 > 1) q[++Q] = Query(1, l1 - 1, l2 - 1, ans);
}
inline void lsh() {
memcpy(b, a, sizeof(int) * (n + 1));
std::sort(b + 1, b + n + 1);
int dis = std::unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i) a[i] = std::lower_bound(b + 1, b + dis + 1, a[i]) - b;
} inline void addl(int x) {
val += cr[a[pre[x]]];
++cl[a[pre[x]]];
}
inline void addr(int x) {
val += cl[a[pre[x]]];
++cr[a[pre[x]]];
}
inline void dell(int x) {
val -= cr[a[pre[x]]];
--cl[a[pre[x]]];
}
inline void delr(int x) {
val -= cl[a[pre[x]]];
--cr[a[pre[x]]];
} inline void work() {
blo = sqrt(n);
std::sort(q + 1, q + Q + 1);
lsh();
int l = 0, r = 0;
for (int i = 1; i <= Q; ++i) {
while (r < q[i].r) addr(++r);
while (l < q[i].l) addl(++l);
while (l > q[i].l) dell(l--);
while (r > q[i].r) delr(r--);
*q[i].ans += q[i].opt * val;
}
for (int i = 1; i <= ansi; ++i) io::print(ans[i]), io::putc('\n');
io::flush();
} inline void init() {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
int x, y;
for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
dfs1(1), dfs2(1, 1);
int rt = 1;
for (int i = 1; i <= m; ++i) {
int opt, x, y;
read(opt);
if (opt == 1) { read(rt); continue; }
++ansi;
read(x), read(y);
if (x == rt) {
if (y == rt) addq(1, n, 1, n, ans + ansi);
else if (!intr(rt, y)) addq(1, n, dfn[y], dfn[y] + siz[y] - 1, ans + ansi);
else {
y = gson(rt, y);
if (dfn[y] > 1) addq(1, n, 1, dfn[y] - 1, ans + ansi);
if (dfn[y] + siz[y] - 1 < n) addq(1, n, dfn[y] + siz[y], n, ans + ansi);
}
}
else if (!intr(rt, x)) {
if (y == rt) addq(dfn[x], dfn[x] + siz[x] - 1, 1, n, ans + ansi);
else if (!intr(rt, y)) addq(dfn[x], dfn[x] + siz[x] - 1, dfn[y], dfn[y] + siz[y] - 1, ans + ansi);
else {
y = gson(rt, y);
if (dfn[y] > 1) addq(dfn[x], dfn[x] + siz[x] - 1, 1, dfn[y] - 1, ans + ansi);
if (dfn[y] + siz[y] - 1 < n) addq(dfn[x], dfn[x] + siz[x] - 1, dfn[y] + siz[y], n, ans + ansi);
}
} else {
x = gson(rt, x);
if (y == rt) {
if (dfn[x] > 1) addq(1, dfn[x] - 1, 1, n, ans + ansi);
if (dfn[x] + siz[x] - 1 < n) addq(dfn[x] + siz[x], n, 1, n, ans + ansi);
} else if (!intr(rt, y)) {
if (dfn[x] > 1) addq(1, dfn[x] - 1, dfn[y], dfn[y] + siz[y] - 1, ans + ansi);
if (dfn[x] + siz[x] - 1 < n) addq(dfn[x] + siz[x], n, dfn[y], dfn[y] + siz[y] - 1, ans + ansi);
} else {
y = gson(rt, y);
if (dfn[x] > 1) {
if (dfn[y] > 1) addq(1, dfn[x] - 1, 1, dfn[y] - 1, ans + ansi);
if (dfn[y] + siz[y] - 1 < n) addq(1, dfn[x] - 1, dfn[y] + siz[y], n, ans + ansi);
}
if (dfn[x] + siz[x] - 1 < n) {
if (dfn[y] > 1) addq(dfn[x] + siz[x], n, 1, dfn[y] - 1, ans + ansi);
if (dfn[y] + siz[y] - 1 < n) addq(dfn[x] + siz[x], n, dfn[y] + siz[y], n, ans + ansi);
}
}
}
}
} int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
// fclose(stdin), fclose(stdout);
return 0;
}

bzoj4940 [Ynoi2016]这是我自己的发明 莫队+dfs序的更多相关文章

  1. [Ynoi2016]这是我自己的发明 莫队

    传送门:here 很棒的莫队题啊..... 题意: 有一棵$ n$个点的树,树上每个点有点权,有$ m$次询问: 操作1:给定两个点$ x,y$,求二元组$ (a,b)$的数量,要求$ a$在$ x$ ...

  2. 洛谷P4689 [Ynoi2016]这是我自己的发明 [莫队]

    传送门 ynoi中比较良心不卡常的题. 思路 没有换根操作时显然可以变成dfs序莫队随便搞. 换根操作时一个子树可以变成两段区间的并集,也随便搞搞就好了. 这题完全不卡常,随便过. 代码 #inclu ...

  3. bzoj4940: [Ynoi2016]这是我自己的发明

    用dfs序把询问表示成询问dfs序的两个区间中的信息 拆成至多9个询问(询问dfs序的两个前缀),对这些询问用莫队处理,时间复杂度$O(n\sqrt{m})$ #include<bits/std ...

  4. 【洛谷 P4688】 [Ynoi2016]掉进兔子洞(bitset,莫队)

    题目链接 第一道Ynoi 显然每次询问的答案为三个区间的长度和减去公共数字个数*3. 如果是公共数字种数的话就能用莫队+bitset存每个区间的状态,然后3个区间按位与就行了. 但现在是个数,bits ...

  5. 【bzoj4940】这是我自己的发明

    Portal --> bzoj4940 Solution (原题这题面到底是..怎么回事啊深深的套路qwq) 感觉自己对根号的算法还是很..没有感觉啊== 实际上这题和bzoj5016没有任何区 ...

  6. luogu P4688 [Ynoi2016]掉进兔子洞 bitset 莫队

    题目链接 luogu P4688 [Ynoi2016]掉进兔子洞 题解 莫队维护bitset区间交个数 代码 // luogu-judger-enable-o2 #include<cmath&g ...

  7. YNOI2016:掉进兔子洞 (莫队+bitset)

    YNOI2016:掉进兔子洞 题意简述: 有 m 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立. 注意这里删掉指的是一个一个删,不是把等于这 ...

  8. [Luogu 4688] [Ynoi2016]掉进兔子洞 (莫队+bitset)

    [Luogu 4688] [Ynoi2016]掉进兔子洞 (莫队+bitset) 题面 一个长为 n 的序列 a.有 m 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间 ...

  9. 【bzoj4940】[Ynoi2016]这是我自己的发明 DFS序+树上倍增+莫队算法

    题目描述 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1. 将树根换为 x. 2. 给出两个点 x,y,从 x 的子树中选每一个点,y 的子树中选每一个点,如果两个点点权相等, ...

随机推荐

  1. linux 文件相关常用命令

    文件或者目录操控命令 1,cd切换目录. 其中- 代表前一个目录 2,mkdir 新建目录. 加上-p参数可以递归创建多级目录 mkdir -p test1/test2/test3 3,rmdir删除 ...

  2. 记一次SQL Server delete语句的优化过程

    今天测试反应问题,性能测试环境一个脚本执行了3个小时没有出结果,期间其他dba已经建立了一些索引但是没有效果. 语句: DELETE T  from License T  WHERE exists ( ...

  3. vue 拖动调整左右两侧div的宽度

    原文链接:https://www.cnblogs.com/layaling/p/11009570.html 原文是左中右三种情况的拖动.由于项目需要,我删除掉了右边的,直接左右区域拖动调整div宽度 ...

  4. dp基础大概 (8.6)

    一些前言: 据说动态规划会用排序,数据结构来进行乱搞优化操作 动态规划滴核心是个啥呢?状态表示和状态转移 设状态:哪些因素会影响到最终答案,就把哪些因素用数组的维度表示出来 要充分描述,也要简洁 举个 ...

  5. 协议-TCP:TCP

    ylbtech-协议-TCP:TCP 传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IETF的RFC 793 ...

  6. Java-小技巧-006 List 排序 Collections.sort()

    一.数组操作类:Arrays 最早开始使用,查看方法二分查找:public static int binarySearch(数据类型[] a, 数据类型 key) 前提是有序的 equals 先排序 ...

  7. 转战 rocketmq

    接触 kafka 有一段时间了,一个人的力量实在有限,国内 rocketmq 的生态确实更好,决定换方向. rocketmq 文档地址:http://rocketmq.cloud/zh-cn/docs ...

  8. 测开之路一百零三:jquery元素和标签的插入与删除

    标签内插入 标签外插入 给标签加标签 !DOCTYPE html><html lang="en"><head> <meta charset=&q ...

  9. http://bbs.csdn.net/topics/340046630 dbgrid怎么获取当前记录值

    这根grid无关,当你选中一条记录时,数据集就定位到这条数据上了,你只需要读取数据集中的数据就行了   对我有用[0] 丢个板砖[0] 引用 | 举报| 管理 hongss hongss 本版等级: ...

  10. Beetle简单构建TCP服务

    使用Beetle构建TCP服务应用是件非常简单的事情,它并不需要你去关注Socket细节,如果你想用Socket编写高性能的TCP服务,那你要关注的东西非常多,异步数据处理,大量连接下的线程管理和连接 ...