bzoj3757 苹果树
3757: 苹果树
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 2403 Solved: 550
[Submit][Status][Discuss]
Description
神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个到n之间的正整数来表示一种颜色。树上一共有n个苹果。每个苹果都被编了号码,号码为一个1到n之间的正整数。我们用0代表树根。只会有一个苹果直接根。
有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。
神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?
Input
输入第一行为两个整数n和m,分别代表树上苹果的个数和前来膜拜的人数。
接下来的一行包含n个数,第i个数代表编号为i的苹果的颜色Coli。
接下来有n行,每行包含两个数x和y,代表有一根树枝连接了苹果x和y(或者根和一个苹果)。
接下来有m行,每行包含四个整数u、v、a和b,代表这个人要数苹果u到苹果v的颜色种数,同时这个人认为颜色a就是颜色b。如果a=b=0,则代表这个人没有患色盲症。
Output
输出一共m行,每行仅包含一个整数,代表这个人应该数出的颜色种数。
Sample Input
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
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
Sample Output
2
1
2
1
2
HINT
0<=x,y,a,b<=N
N<=50000
1<=U,V,Coli<=N
M<=100000
此题存在版权,故不再支持提交,保留在此只供大家参考题面! 望见谅!
分析:复习了一遍树上莫队.关于树上莫队算法可见:bzoj3052.
一个易错点:点x的位置变成了x的dfs序,而不是x的编号.
#include <cstdio> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1000010; int n,m,col[maxn],head[maxn],to[maxn],nextt[maxn],tot = 1,ecnt,belong[maxn],sta[maxn],top; int block,root,fa[maxn][20],tong[maxn],vis[maxn],L = 1,R = 0,deep[maxn],anss[maxn],ans,pos[maxn],dfs_clock; struct node { int l,r,a,b,id; }e[maxn]; void update1(int temp,int x) { if (vis[x]) { vis[x] = 0; tong[col[x]]--; if (tong[col[x]] == 0) ans--; } else { vis[x] = 1; if (tong[col[x]] == 0) ans++; tong[col[x]]++; } } void update2(int temp,int l,int r) { while (l != r) { if (deep[l] < deep[r]) { update1(temp,r); r = fa[r][0]; } else { update1(temp,l); l = fa[l][0]; } } } bool cmp(node a,node b) { int apos = belong[a.l],bpos = belong[b.l]; if (apos == bpos) return pos[a.r] < pos[b.r]; return apos < bpos; } void add(int x,int y) { to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } int lca(int x,int y) { if (deep[x] < deep[y]) swap(x,y); for (int i = 19; i >= 0; i--) if (deep[fa[x][i]] >= deep[y]) x = fa[x][i]; if (x == y) return x; for (int i = 19; i >= 0; i--) if (fa[x][i] != fa[y][i]) { x = fa[x][i]; y = fa[y][i]; } return fa[x][0]; } void solve() { update2(1,e[1].l,e[1].r); int LCA = lca(e[1].l,e[1].r); update1(1,LCA); anss[e[1].id] = ans; if (tong[e[1].a] && tong[e[1].b] && e[1].a != e[1].b) anss[e[1].id]--; update1(1,LCA); for (int i = 2; i <= m; i++) { update2(i,e[i - 1].l,e[i].l); update2(i,e[i - 1].r,e[i].r); LCA = lca(e[i].l,e[i].r); update1(i,LCA); anss[e[i].id] = ans; if (tong[e[i].a] && tong[e[i].b] && e[i].a != e[i].b) anss[e[i].id]--; update1(i,LCA); } } int dfs(int u,int faa) { int left = 0; pos[u] = ++dfs_clock; fa[u][0] = faa; deep[u] = deep[faa] + 1; for (int i = head[u]; i; i = nextt[i]) { int v = to[i]; if (v == faa) continue; left += dfs(v,u); if (left >= block) { ecnt++; while (left) { belong[sta[top--]] = ecnt; left--; } } } sta[++top] = u; return left + 1; } int main() { scanf("%d%d",&n,&m); block = pow(n,2.0 / 3.0); for (int i = 1; i <= n; i++) scanf("%d",&col[i]); for (int i = 1; i <= n; i++) { int x,y; scanf("%d%d",&x,&y); if (x == 0 || y == 0) { if (x == 0) root = y; else root = x; } else { add(x,y); add(y,x); } } dfs(root,0);while (top) belong[sta[top--]] = ecnt; for (int j = 1; j <= 19; j++) for (int i = 1; i <= n; i++) fa[i][j] = fa[fa[i][j - 1]][j - 1]; for (int i = 1; i <= m; i++) { scanf("%d%d%d%d",&e[i].l,&e[i].r,&e[i].a,&e[i].b); if (pos[e[i].l] > pos[e[i].r]) swap(e[i].l,e[i].r); e[i].id = i; } sort(e + 1,e + 1 + m,cmp); solve(); for (int i = 1; i <= m; i++) printf("%d\n",anss[i]); return 0; }