UOJ33 [UR #2] 树上GCD 【点分治】【容斥原理】【分块】

题目分析:

树上点对问题首先想到点分治。假设我们进行了点分治并递归地解决了子问题。现在我们合并问题。

我们需要找到所有经过当前重心$ c $的子树路径。第一种情况是LCA为当前重心$ c $。考虑以$ 1 $为根的$ c $的子树。那么首先在子问题中先斥掉不经过$ c $的路径。然后对于$ c $的子树处理出距离数组。用桶存储。

从大到小枚举最大公因数$ d $,求出所有距离为$ d $倍数的点的个数。然后做乘法得到$ num1 $。再考虑$ num1 $中GCD不等于$ d $的数有哪些。实际上我们早就计算出了GCD为$ d $的倍数的情况了,只需要把这一部分斥掉就行了。所以处理$ c $的子树的点对的时间复杂度是$ O(nlogn) $。

再考虑$ c $的祖先的儿子到$ c $的子树中的点的情况。不难想到类似的处理方法。开个桶存储距离。由于点分治的特性。我们很容易就可以证明这样所有桶的最大值之和是不会超过$ O(n) $的。这样枚举的时间复杂度是有保障的。对于$ c $的某个祖先的子树中的点,枚举GCD为$ d $。 那么我们要在$ c $的子树中找到所有的距$ c $祖先$ d $的倍数的点。 由于我们拔高了LCA,这时候我们不能简单地枚举倍数。重新审视问题,发现其实它是求的c中从i开始每隔j个的和。采用分块算法,对于小于等于$ \sqrt{n} $的情况共有$ \sqrt{n} $个不同的起点。每个不同的间隔都会完全覆盖依次整个桶。共覆盖了$ \sqrt{n} $次。所以预处理的时间复杂度为$ O(n*\sqrt{n}) $。对于大于$ \sqrt{n} $的情况可以暴力计算。 

我们每处理一个重心的时间复杂度为$ O(n*\log n+n*\sqrt{n}) $分为两半,前半部分总时间复杂度为$ O(n*\log n*\log n) $,后半部分带入主定理知为$ O(n*\sqrt{n}) $

 

代码:

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 const int maxn = 200005;
  5 
  6 int n;
  7 int dep[maxn],f[maxn];
  8 long long ans[maxn];
  9 vector <int> g[maxn];
 10 
 11 int arr[maxn],sz[maxn],dg[maxn],presum[maxn];
 12 
 13 void read(){
 14     scanf("%d",&n);
 15     for(int i=2;i<=n;i++) {
 16     scanf("%d",&f[i]);
 17     g[f[i]].push_back(i);
 18     g[i].push_back(f[i]);
 19     }
 20 }
 21 
 22 void DFS(int now,int dp) {
 23     dep[now] = dp;
 24     for(int i=0;i<g[now].size();i++){
 25     if(g[now][i] == f[now]) continue;
 26     DFS(g[now][i],dp+1);
 27     }
 28 }
 29 
 30 void dfs1(int now,int fa){
 31     sz[now] = 0;dg[now] = 0;
 32     for(int i=0;i<g[now].size();i++){
 33     if(g[now][i] == fa) continue;
 34     if(arr[g[now][i]]) continue;
 35     dfs1(g[now][i],now);
 36     sz[now] += sz[g[now][i]];
 37     dg[now] = max(dg[now],sz[g[now][i]]);
 38     }
 39     sz[now]++;
 40 }
 41 
 42 int dfs2(int now,int fa,int tot){
 43     int res = now;dg[now] = max(tot-sz[now],dg[now]);
 44     for(int i=0;i<g[now].size();i++){
 45     if(g[now][i] == fa) continue;
 46     if(arr[g[now][i]]) continue;
 47     int z = dfs2(g[now][i],now,tot);
 48     if(dg[res] > dg[z])res = z;
 49     }
 50     return res;
 51 }
 52 
 53 int depest,h[maxn],sum[maxn];
 54 long long nowans[maxn];
 55 int Final[500][500];
 56 
 57 int oth,arv[maxn],s2[maxn];
 58 
 59 void dfs3(int now,int len,int stop){
 60     h[len]++;depest = max(depest,len);
 61     for(int i=0;i<g[now].size();i++){
 62     if(g[now][i] == f[now]) continue;
 63     if(arr[g[now][i]]!=0 && arr[g[now][i]]<=stop) continue;
 64     dfs3(g[now][i],len+1,stop);
 65     }
 66 }
 67 
 68 void dfs4(int now,int cant,int stop,int lca){
 69     arv[dep[now]-dep[lca]]++; oth = max(oth,dep[now]-dep[lca]);
 70     for(int i=0;i<g[now].size();i++){
 71     if(g[now][i] == f[now] || g[now][i] == cant) continue;
 72     if(arr[g[now][i]]!=0 && arr[g[now][i]]<=stop) continue;
 73     dfs4(g[now][i],cant,stop,lca);
 74     }
 75 }
 76 
 77 void solve_dec(int now,int last,int ht){
 78     depest = 0;
 79     dfs3(now,1,ht);
 80     for(int i=depest;i>=1;i--){
 81     for(int j=1;i*j<=depest;j++) sum[i] += h[i*j];
 82     nowans[i] = 1ll*sum[i]*(sum[i]-1)/2;
 83     for(int j=2;i*j<=depest;j++) nowans[i]-=nowans[i*j];
 84     ans[i] -= nowans[i];
 85     }
 86     for(int i=0;i<=depest;i++) h[i] = 0,sum[i] = 0,nowans[i] = 0;
 87 }
 88 
 89 void solve_add(int now,int ht){
 90     depest = 0;
 91     dfs3(now,0,ht);
 92     for(int i=depest;i>=1;i--){
 93     for(int j=1;i*j<=depest;j++) sum[i] += h[i*j]; //multi d
 94     nowans[i] = 1ll*sum[i]*(sum[i]-1)/2;
 95     for(int j=2;i*j<=depest;j++) nowans[i]-=nowans[i*j];
 96     ans[i] += nowans[i];
 97     }
 98     for(int i=1;i<=depest;i++) nowans[i] = 0;
 99     
100     for(int i=1;i*i<=depest;i++) for(int j=0;j<i;j++)
101         for(int k=j;k<=depest;k+=i) Final[i][j] += h[k];
102     // in subtree
103     
104     int tp = f[now],last = now,rem = 0;
105     while(tp&&(arr[tp]>ht||arr[tp] == 0)){
106     oth = 0;rem++;
107     dfs4(tp,last,ht,tp); // tp mean lca
108     for(int i=oth;i>=1;i--){
109         for(int j=1;i*j<=oth;j++) s2[i] += arv[i*j];
110         if(i*i<=depest) 
111         nowans[i] = 1ll*s2[i]*Final[i][(((-rem)%i)+i)%i];
112         else{
113         int frst = (((-rem)%i)+i)%i;
114         int tot = 0;
115         for(int k =frst;k<=depest;k+=i) tot += h[k];
116         nowans[i] = 1ll*s2[i]*tot;
117         }
118         for(int j=2;i*j<=oth;j++) nowans[i] -= nowans[i*j];
119         ans[i] += nowans[i];
120     }
121     last = tp; tp = f[tp];
122     for(int i=0;i<=oth;i++) arv[i] = s2[i] = nowans[i] = 0;
123     }
124     //out subtree
125     for(int i=0;i<=depest;i++) h[i] = 0,sum[i] = 0;
126     for(int i=1;i*i<=depest;i++) for(int j=0;j<i;j++) Final[i][j] = 0;
127 }
128 
129 void divide(int now,int last,int ht){
130     dfs1(now,0); int pw = sz[now];
131     int pt = dfs2(now,0,pw);
132     arr[pt] = ht;
133     for(int i=0;i<g[pt].size();i++){
134     if(arr[g[pt][i]]) continue;
135     divide(g[pt][i],pt,ht+1);
136     }
137     if(last&&f[now]==last) solve_dec(now,last,ht-1);
138     solve_add(pt,ht);
139 }
140 
141 void work(){
142     DFS(1,1);
143     divide(1,0,1);
144     for(int i=1;i<=n;i++) presum[dep[i]-1]++;
145     for(int i=n;i>=1;i--) presum[i] += presum[i+1];
146     for(int i=1;i<=n;i++) ans[i] += presum[i];
147     for(int i=1;i<n;i++) printf("%lld\n",ans[i]);
148 }
149 
150 
151 int main(){
152     read();
153     work();
154     return 0;
155 }

 

转载于:https://www.cnblogs.com/Menhera/p/9082906.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值