BZOJ传送门
题目描述
神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个到 n n 之间的正整数来表示一种颜色。树上一共有个苹果。每个苹果都被编了号码,号码为一个 1 1 到之间的正整数。我们用 0 0 代表树根。只会有一个苹果直接根。
有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果 u u 和苹果的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色 a a 就是颜色,那么他们在数苹果的颜色时,如果既出现了颜色 a a 的苹果,又出现了颜色的苹果,这个人只会算入颜色 b b ,而不会把颜色算进来。
神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?
输入输出格式
输入格式
输入第一行为两个整数 n n 和,分别代表树上苹果的个数和前来膜拜的人数。
接下来的一行包含 n n 个数,第i个数代表编号为i的苹果的颜色。
接下来有 n n 行,每行包含两个数和 y y ,代表有一根树枝连接了苹果和 y y (或者根和一个苹果)。
接下来有行,每行包含四个整数 u、v、a u 、 v 、 a 和 b b ,代表这个人要数苹果到苹果 v v 的颜色种数,同时这个人认为颜色就是颜色 b b 。如果,则代表这个人没有患色盲症。
输出格式
输出一共 m m 行,每行仅包含一个整数,代表这个人应该数出的颜色种数。
输入样例
5 3
1 1 3 3 2
0 1
1 2
1 3
2 4
3 5
1 4 0 0
1 4 1 3
1 4 1 2
输出样例
2
1
2
数据范围
N≤50000
N
≤
50000
1≤U,V,Coli≤N
1
≤
U
,
V
,
C
o
l
i
≤
N
M≤100000
M
≤
100000
解题分析
蒟蒻A的树上莫队的第一道题…
考虑按王室联邦的方法对树进行分块, 这样分块后树上莫队就变成了序列莫队。那么如何从
(x1,y1)
(
x
1
,
y
1
)
转移至
(x2,y2)
(
x
2
,
y
2
)
? 画图后不难发现只需要将
x1
x
1
至
x2
x
2
和
y1
y
1
至
y2
y
2
路径上面的状态取反即可。 不过因为
lca
l
c
a
会被讨论两次, 所以不如单独计算。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100010
template <class T>
IN void in (T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int head[MX], col[MX], ans[MX], seg[MX], st[MX], fat[MX], topf[MX], son[MX], siz[MX], cot[MX], dep[MX];
int block, q, dot, top, arr, res, cnt;
bool ex[MX];
struct Edge
{
int to, nex;
}edge[MX];
IN void addedge(const int &from, const int &to)
{
edge[++cnt] = {to, head[from]};
head[from] = cnt;
}
struct Que
{
int lef, rig, x, y, id;
}eve[MX];
IN bool operator < (const Que &x, const Que &y)
{//同样是奇偶优化
if(seg[x.lef] != seg[y.lef]) return seg[x.lef] < seg[y.lef];
return (seg[x.lef] & 1) ? seg[x.rig] < seg[y.rig] : seg[x.rig] > seg[y.rig];
}
void DFS(const int &now)//树剖LCA和分块一起写
{
int bot = top; siz[now] = 1;
for (int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fat[now]) continue;
dep[edge[i].to] = dep[now] + 1;
fat[edge[i].to] = now;
DFS(edge[i].to);
siz[now] += siz[edge[i].to];
if(siz[edge[i].to] > siz[son[now]]) son[now] = edge[i].to;
if(top - bot >= block)
{
++arr;
W (top > bot) seg[st[top--]] = arr;
}
}
st[++top] = now;
}
void DFS(const int &now, const int &grand)//树剖的另一个DFS
{
topf[now] = grand;
if(son[now] == 100001) return;
DFS(son[now], grand);
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fat[now] || edge[i].to == son[now]) continue;
DFS(edge[i].to, edge[i].to);
}
}
IN int LCA(R int x, R int y)
{
W (topf[x] != topf[y])
{
if(dep[topf[x]] < dep[topf[y]]) std::swap(x, y);
x = fat[topf[x]];
}
return dep[x] > dep[y] ? y : x;
}
IN void modify(const int &now)
{
if(!ex[now]) {++cot[col[now]]; if(cot[col[now]] == 1) ++res;}
else {--cot[col[now]]; if(!cot[col[now]]) --res;}
ex[now] ^= 1;
}
IN void cal(R int x, R int y)
{
W (x ^ y)//暴力往上跳即可
{
if(dep[x] > dep[y]) modify(x), x = fat[x];
else modify(y), y = fat[y];
}
}
int main(void)
{
std::fill(son, son + 100005, 100001);//有0点, 所以赋为一个不会重的位置
int a, b, lca, lb, rb;
in(dot), in(q); block = sqrt(dot) + 1;
for (R int i = 1; i <= dot; ++i) in(col[i]);
for (R int i = 1; i <= dot; ++i)
in(a), in(b), addedge(a, b), addedge(b, a);
dep[0] = 1;
DFS(0);
W (top) seg[top--] = arr;
DFS(0, 0);
for (R int i = 1; i <= q; ++i)
{
in(eve[i].lef), in(eve[i].rig), in(eve[i].x), in(eve[i].y); eve[i].id = i;
if(seg[eve[i].lef] > seg[eve[i].rig]) std::swap(eve[i].lef, eve[i].rig);
}
std::sort(eve + 1, eve + 1 + q); lca = LCA(eve[1].lef, eve[1].rig);
cal(eve[1].lef, eve[1].rig); modify(lca);//单独处理LCA
ans[eve[1].id] = res;
if(eve[1].x != eve[1].y && cot[eve[1].x] && cot[eve[1].y]) --ans[eve[1].id];
modify(lca), lb = eve[1].lef, rb = eve[1].rig;
for (R int i = 2; i <= q; ++i)
{
cal(lb, eve[i].lef), cal(rb, eve[i].rig);
lca = LCA(eve[i].lef, eve[i].rig);
modify(lca);
ans[eve[i].id] = res;
if(eve[i].x != eve[i].y && cot[eve[i].x] && cot[eve[i].y]) --ans[eve[i].id];
modify(lca); lb = eve[i].lef, rb = eve[i].rig;
}
for (R int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
}