codeforces 914E 树上点分治

r囧r小猫 2021-12-17 12:19 359阅读 0赞

https://codeforc.es/contest/914/problem/E

题解:

首先,这个是一个可减的信息,需要容斥去做

对于信息而言,显然是状压保存,然后用数组去记录出现次数

对于一个点,当前点的ans是这样统计的:

1.计算整棵树的贡献,

2.对于根的每个儿子,先删除他的贡献,然后计算一次该子树的贡献,然后再加回来

3.删除整棵树的贡献

由于dis(a,b)和dis(b,a)会重复统计,因此对于根的贡献要/2

而其他节点,由于是走一步算一步,因此只会算一遍

  1. #include<bits/stdc++.h>
  2. #define endl '\n'
  3. #define ll long long
  4. #define pii pair<ll,int>
  5. #define all(x) x.begin(),x.end()
  6. #define IO ios::sync_with_stdio(false)
  7. #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
  8. #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
  9. #define forn(i,x) for(int i=head[x];i;i=e[i].next)
  10. using namespace std;
  11. const int maxn=4e5+10,maxm=4e5+10;
  12. const int INF=0x3f3f3f3f;
  13. const int mod=1e9+7;
  14. const double PI=acos(-1.0);
  15. int casn,n,m;
  16. ll k;
  17. string s;
  18. int num[maxn];
  19. class graph{public:
  20. struct node{int to,next;ll cost;}e[maxm];
  21. int head[maxn],nume,n,sz[maxn],maxt,stree[maxn];
  22. void add(int a,int b,ll c=0){e[++nume]={b,head[a],c};head[a]=nume;}
  23. int vis[maxn],all,mid;
  24. void getmid(int now=1,int pre=0){
  25. sz[now]=1;
  26. for(int i=head[now];i;i=e[i].next){
  27. if(e[i].to==pre||vis[e[i].to]) continue;
  28. getmid(e[i].to,now);
  29. sz[now]+=sz[e[i].to];
  30. }
  31. int tmp=max(sz[now]-1,all-sz[now]);
  32. if(maxt>tmp) maxt=tmp,mid=now;
  33. }//base
  34. int cnt[1<<20|10];
  35. ll ans[maxn];
  36. void init(int n){
  37. this->n=n,nume=1,mid=0;
  38. rep(i,1,n) vis[i]=head[i]=0;
  39. }
  40. void update(int now,int pre,int flag,int d){
  41. d^=num[now];cnt[d]+=flag;
  42. for(int i=head[now];i;i=e[i].next){
  43. int to=e[i].to;
  44. if(to==pre||vis[to]) continue;
  45. update(to,now,flag,d);
  46. }
  47. }
  48. ll getdis(int now,int pre,int d){
  49. d^=num[now];ll sum=cnt[d];
  50. rep(i,0,19) sum+=cnt[d^(1<<i)];
  51. for(int i=head[now];i;i=e[i].next){
  52. int to=e[i].to;
  53. if(to==pre||vis[to]) continue;
  54. sum+=getdis(to,now,d);
  55. }
  56. ans[now]+=sum;
  57. return sum;
  58. }
  59. void getans(int now){
  60. update(now,0,1,0);
  61. ll sum=cnt[0];
  62. rep(i,0,19) sum+=cnt[1<<i];
  63. for(int i=head[now];i;i=e[i].next){
  64. int to=e[i].to;
  65. if(vis[to]) continue;
  66. update(to,now,-1,num[now]);
  67. sum+=getdis(to,now,0);
  68. update(to,now,1,num[now]);
  69. }
  70. update(now,0,-1,0);
  71. ans[now]+=sum/2;
  72. }
  73. void divide(int now){
  74. vis[now]=1;getans(now);
  75. for(int i=head[now];i;i=e[i].next){
  76. int to=e[i].to;
  77. if(vis[to]) continue;
  78. all=sz[to],maxt=n+1;
  79. getmid(to,now);divide(mid);
  80. }
  81. }
  82. void solve(){
  83. maxt=all=n;memset(ans,0,(sizeof ans[0])*(n+1));
  84. getmid();divide(mid);
  85. }
  86. }g;
  87. int main() {IO;
  88. cin>>n;
  89. g.init(n);
  90. rep(i,2,n){
  91. int a,b;cin>>a>>b;
  92. g.add(a,b);g.add(b,a);
  93. }
  94. cin>>s;s=" "+s;
  95. rep(i,1,n){
  96. num[i]=(1<<(s[i]-'a'));
  97. }
  98. g.solve();
  99. rep(i,1,n) cout<<g.ans[i]+1<<' ';
  100. }

转载于:https://www.cnblogs.com/nervendnig/p/10911674.html

发表评论

表情:
评论列表 (有 0 条评论,359人围观)

还没有评论,来说两句吧...

相关阅读

    相关 BZOJ1095 动态分治()

    题意: 操作1.修改一个点的颜色(黑白互换) 操作2.询问所有黑色点之间最远距离   点分树:当我们可以形如点分治一样的统计答案,即每次确定一个重心,然后计算他们子树之

    相关 Codeforces 750E 线段DP

    题意:给你一个字符串,有两种操作:1:把某个位置的字符改变。2:询问l到r的子串最少需要删除多少个字符,使得这个子串含有2017子序列,并且没有2016子序列? 思路:线段树

    相关 Codeforces 1101D 分治

    题意:有一颗树,每个点有一个点权,边权都是1,问路径上的所有点的gcd不是1的最长路径是多少? 思路:之前补这道题的时候,用分解质因数 + 树形DP做的,其实用点分治可以更暴