【NOIP2017提高A组模拟9.14】生命之树 (dsu on tree+trie)
2017-09-18 12:21
453 查看
Description:
1<=n<=105,|S|<=5∗105
题解:
这是一道树上启发式的题目,也算是我做得树上启发式的第一题,这次比赛一下出现了两题树上启发式,都不会,让人无语。看到xor,马上拆位,这个都是老套路了。
对于一个点u,把它和它的子树的字符串全部一次丢到一个trie里,因为已经拆了位,随便维护一下就可以求出答案。
但是我们知道这样暴力是很慢的,复杂度易得。
利用启发式可以把暴力的复杂度优化。
大概流程如下:
对于一个点x,先计算其轻儿子的答案,做完以后,全局的trie是清空的。
接着计算其重儿子的答案,注意此时重儿子的所有子树的节点的字符串是在trie里。
接着再把x的除重儿子子树外的所有节点加入trie,x也要加进trie里。
这样x的子树的答案就已经计算出来了。
如果x是其父亲节点的重儿子,保留当前的trie,否则清空trie。
我们可以分析一下复杂度:
一个点被加入trie的次数就是它到根的路径上的轻重链数的总和,有加入才有删除。
所以复杂度是:O(|S|∗log n)
Code:
#include<cstdio> #include<cstring> #define ll long long #define fo(i, x, y) for(int i = x; i <= y; i ++) using namespace std; const int N = 500005; char s , S ; int a2[17], w; int n, x, y, a [17], l , r ; int final , tot; struct edge { int to, next; }e[N * 2]; int fa , bz , siz , son ; int next [26], tt = 1, root = 1; ll ans , ans2 , sm [2]; void link(int x, int y) { e[++ tot].next = final[x], e[tot].to = y, final[x] = tot; e[++ tot].next = final[y], e[tot].to = x, final[y] = tot; } void dg(int x) { bz[x] = 1; siz[x] = 1; for(int i = final[x]; i; i = e[i].next) { int y = e[i].to; if(bz[y]) continue; fa[y] = x; dg(y); siz[x] += siz[y]; son[x] = siz[y] > siz[son[x]] ? y : son[x]; } bz[x] = 0; } ll Insert(int x) { int y = root; ll sum = 0; fo(i, l[x], r[x]) { int c = s[i] - 'a'; if(next[y][c] == 0) next[y][c] = ++ tt; y = next[y][c]; sum += sm[y][!a[x][w]] * a2[w]; sm[y][a[x][w]] ++; } return sum; } ll dfs(int x) { bz[x] = 1; ll sum = Insert(x); for(int i = final[x]; i; i = e[i].next) { int y = e[i].to; if(bz[y]) continue; sum += dfs(y); } bz[x] = 0; return sum; } void dg2(int x) { bz[x] = 1; for(int i = final[x]; i; i = e[i].next) { int y = e[i].to; if(bz[y] || y == son[x]) continue; dg2(y); } if(son[x] != 0) dg2(son[x]), ans[x] += ans[son[x]]; for(int i = final[x]; i; i = e[i].next) { int y = e[i].to; if(bz[y] || y == son[x]) continue; ans[x] += dfs(y); } ans[x] += Insert(x); if(x != son[fa[x]]) { fo(i, 1, tt) memset(next[i], 0, sizeof(next[i])), sm[i][0] = sm[i][1] = 0; tt = 1; } bz[x] = 0; } int main() { freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout); scanf("%d", &n); fo(i, 1, n) { scanf("%d", &x); fo(j, 0, 16) a[i][j] = x & 1, x /= 2; } fo(i, 1, n) { scanf("%s", S + 1); int len = strlen(S + 1); l[i] = r[i - 1] + 1; r[i] = r[i - 1] + len; fo(j, l[i], r[i]) s[j] = S[j - l[i] + 1]; } fo(i, 1, n - 1) { scanf("%d %d", &x, &y); link(x, y); } dg(1); a2[0] = 1; fo(i, 1, 16) a2[i] = a2[i - 1] * 2; for(w = 0; w <= 16; w ++) { dg2(1); fo(i, 1, n) ans2[i] += ans[i], ans[i] = 0; } fo(i, 1, n) printf("%lld\n", ans2[i]); }
相关文章推荐
- jzoj5363【NOIP2017提高A组模拟9.14】生命之树 trie+启发式合并
- 【NOIP2017提高A组模拟9.14】生命之树 trie+启发式合并
- codeforces 208 E. Blood Cousins (dsu on the tree)
- [Codeforces741D]Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on the tree)
- 【HDU - 6191 】Query on A Tree 【 可持久01Trie +DFS序 】
- (前缀和排序)NOIP2017提高组模拟 Day1P1 小A的数学
- 【NOIP2017提高A组模拟8.25】影子
- [trick]dsu on tree
- HDU - 6191 - Query on A Tree (可持久化Trie)
- B【NOIP2017提高组模拟12.18】
- 【NOIP2017提高组模拟12.17】向再见说再见
- [Codeforces375D]Tree and Queries(dsu on the tree+bit)
- Codeforces 600E Lomsat gelral [dsu on tree(树上启发式合并)]
- 【NOIP2017提高A组模拟8.10】文本编辑器
- JZOJ 4778. 【NOIP2016提高A组模拟9.14】数列编辑器
- CF 208E. Blood Cousins [dsu on tree 倍增]
- 【NOIP2016提高A组模拟9.14】灌水
- JZOJ 4919.【NOIP2017提高组模拟12.10】神炎皇
- 【NOIP2017提高组模拟12.10】幻魔皇
- 【NOIP2017提高A组模拟9.5】心灵治愈