BZOJ 3757: 苹果树
3757: 苹果树
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1726 Solved: 550
[Submit][Status][Discuss]
Description
神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个到n之间的正整数来表示一种颜色。树上一共有n个苹果。每个苹果都被编了号码,号码为一个1到n之间的正整数。我们用0代表树根。只会有一个苹果直接根。
有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。
神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?
Input
Output
输出一共m行,每行仅包含一个整数,代表这个人应该数出的颜色种数。
Sample Input
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
1
2
HINT
Source
树上莫队的模板题。
仿照序列莫队的方式对树进行适当的sqrt(n)分块,然后按照区间左端点的所在块分组;每组内按照右端点的DFS序排序。易知两个组内询问转移答案时,左端点至多移动O(sqrt(n)),而右端点全局是O(n)的,复杂度O(Nsqrt(N))。至于维护哪些点以及如何转移,网上有很详细的解释。
1 #include <bits/stdc++.h> 2 3 inline int nextInt(void) { 4 register int ret = 0; 5 register int neg = 0; 6 register int bit = getchar(); 7 8 while (bit < '0') { 9 if (bit == '-') 10 neg ^= 1; 11 bit = getchar(); 12 } 13 14 while (bit >= '0') { 15 ret = ret*10 + bit - '0'; 16 bit = getchar(); 17 } 18 19 return neg ? -ret : ret; 20 } 21 22 const int siz = 1000005; 23 24 int n, m, s, col[siz], ans[siz]; 25 26 int hd[siz], nt[siz], to[siz], tot; 27 28 int fa[siz][20], dp[siz], dn[siz], tim; 29 30 int sz[siz], bl[siz], cnt, vs[siz], ct[siz], sum; 31 32 void dfs(int u, int f) { 33 fa[u][0] = f; 34 dn[u] = ++tim; 35 dp[u] = dp[f] + 1; 36 37 for (int i = 1; i < 20; ++i) 38 fa[u][i] = fa[fa[u][i - 1]][i - 1]; 39 40 for (int i = hd[u]; i; i = nt[i])if (to[i] != f) { 41 if (sz[bl[u]] < s) 42 ++sz[bl[to[i]] = bl[u]]; 43 else 44 ++sz[bl[to[i]] = ++cnt]; 45 dfs(to[i], u); 46 } 47 } 48 49 inline int lca(int a, int b) { 50 if (dp[a] < dp[b])std::swap(a, b); 51 52 for (int i = 19; ~i; --i) 53 if (dp[fa[a][i]] >= dp[b]) 54 a = fa[a][i]; 55 56 if (a == b)return a; 57 58 for (int i = 19; ~i; --i) 59 if (fa[a][i] != fa[b][i]) 60 a = fa[a][i], b = fa[b][i]; 61 62 return fa[a][0]; 63 } 64 65 struct query { 66 int l, r, a, b, id; 67 68 void read(int i) { 69 id = i; 70 l = nextInt(); 71 r = nextInt(); 72 a = nextInt(); 73 b = nextInt(); 74 75 if (dn[l] > dn[r]) 76 std::swap(l, r); 77 } 78 }qry[siz]; 79 80 inline bool cmp_query(const query &A, const query &B) { 81 if (bl[A.l] != bl[B.l]) 82 return bl[A.l] < bl[B.l]; 83 else 84 return dn[A.r] < dn[B.r]; 85 } 86 87 inline void change(int t) { 88 vs[t] ^= 1; 89 if (vs[t]) 90 sum += (++ct[col[t]] == 1); 91 else 92 sum -= (--ct[col[t]] == 0); 93 } 94 95 inline void change(int a, int b) { 96 int L = lca(a, b); 97 98 while (a != L) 99 change(a), a = fa[a][0]; 100 101 while (b != L) 102 change(b), b = fa[b][0]; 103 } 104 105 int L, R, LCA; // Path(L,R) ^ LCA 106 107 inline void solve(const query &q) { 108 change(L, q.l); L = q.l; 109 change(R, q.r); R = q.r; 110 LCA = lca(L, R); 111 change(LCA); 112 ans[q.id] = sum; 113 if (q.a != q.b) 114 if (ct[q.a] && ct[q.b]) 115 --ans[q.id]; 116 change(LCA); 117 } 118 119 signed main(void) { 120 // freopen("apple.in", "r", stdin); 121 // freopen("apple.out", "w", stdout); 122 123 n = nextInt(); 124 m = nextInt(); 125 126 for (int i = 1; i <= n; ++i) 127 col[i] = nextInt(); 128 129 for (int i = 1; i <= n; ++i) { 130 int x = nextInt(); 131 int y = nextInt(); 132 nt[++tot] = hd[x]; to[tot] = y; hd[x] = tot; 133 nt[++tot] = hd[y]; to[tot] = x; hd[y] = tot; 134 } 135 136 L = R = to[hd[0]]; s = sqrt(n); 137 138 sz[bl[0] = cnt = 1] = 1; dfs(0, 0); 139 140 for (int i = 1; i <= m; ++i)qry[i].read(i); 141 142 std::sort(qry + 1, qry + 1 + m, cmp_query); 143 144 for (int i = 1; i <= m; ++i)solve(qry[i]); 145 146 for (int i = 1; i <= m; ++i)printf("%d\n", ans[i]); 147 }
因为BZOJ上已经不再提供测试,submit之后也只能得到RE的结果,只好自己造几个数据,就当AC了。
@Author: YouSiki