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 }