1. 程式人生 > 其它 >7.5下午,洛谷多校1部分題目

7.5下午,洛谷多校1部分題目

我只寫了H題。

題意:給定一棵樹,每個點有顏色,以1為根,對每個子樹求子樹內相同顏色點兩兩之間的距離的和。

n,顏色數量<=10^5

題解:

暴力地想,每對點的貢獻記錄在它們的lca上,然後每個點的答案貢獻到它到根的路徑上就好了,前者的複雜度用暴力是O(n^2),後者是O(n)

一開始的想法是樹鏈剖分,但是挺難寫的,於是想了一個分類討論的做法。

記錄每個顏色的點的個數,如果該顏色的點不大於x,那麼我直接暴力,大於x的,用dfs在O(n)的時間內遍歷一次求單顏色的答案,這樣複雜度最壞是O(x*x*k1+k2*n),x取√n可以保證複雜度在O(n√n)。

 1 #include<bits/stdc++.h>
 2
using namespace std; 3 const int N=1e5+10; 4 int tot=0,he[N],ne[N*2],t[N*2]; 5 void link(int x,int y) 6 { 7 tot++; 8 ne[tot]=he[x]; 9 he[x]=tot; 10 t[tot]=y; 11 } 12 int tot1=0,he1[N],ne1[N*2],t1[N*2]; 13 void link1(int x,int y) 14 { 15 tot1++; 16 ne1[tot1]=he1[x]; 17 he1[x]=tot1;
18 t1[tot1]=y; 19 } 20 int dep[N],f[N][20]; 21 void dfs1(int x,int y) 22 { 23 dep[x]=dep[y]+1; 24 f[x][0]=y; 25 for (int i=1;i<=17;i++) f[x][i]=f[f[x][i-1]][i-1]; 26 for (int i=he[x];i;i=ne[i]) if (t[i]!=y) dfs1(t[i],x); 27 } 28 int lca(int x,int y) 29 { 30 if (dep[x]<dep[y]) swap(x,y);
31 for (int i=17;i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i]; 32 if (x==y) return x; 33 for (int i=17;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 34 return f[x][0]; 35 } 36 int c2[N],a[N],c[N],d[N]; 37 long long c1[N],ans[N]; 38 void dfs(int x,int z,int y) 39 { 40 c1[x]=0; 41 c2[x]=0; 42 if (a[x]==z) c1[x]=dep[x],c2[x]=1; 43 for (int i=he[x];i;i=ne[i]) if (t[i]!=y) 44 { 45 dfs(t[i],z,x); 46 ans[x]+=1ll*(c1[x]-1ll*c2[x]*dep[x])*c2[t[i]]+1ll*(c1[t[i]]-1ll*c2[t[i]]*dep[x])*c2[x]; 47 c1[x]=c1[x]+c1[t[i]]; 48 c2[x]=c2[x]+c2[t[i]]; 49 } 50 } 51 void flow(int x,int y) 52 { 53 for (int i=he[x];i;i=ne[i]) if (t[i]!=y) 54 { 55 flow(t[i],x); 56 ans[x]=ans[x]+ans[t[i]]; 57 } 58 } 59 int main() 60 { 61 int n; 62 scanf("%d",&n); 63 for (int i=1;i<=n;i++) 64 { 65 scanf("%d",&a[i]); 66 c[a[i]]++; 67 link1(a[i],i); 68 } 69 for (int i=1;i<n;i++) 70 { 71 int x,y; 72 scanf("%d%d",&x,&y); 73 link(x,y); 74 link(y,x); 75 } 76 dfs1(1,0); 77 for (int i=1;i<=n;i++) 78 { 79 if (1ll*c[i]*c[i]<=n) 80 { 81 int k=0; 82 for (int j=he1[i];j;j=ne1[j]) 83 { 84 k++; 85 d[k]=t1[j]; 86 } 87 for (int j=1;j<=k;j++) for (int l=j+1;l<=k;l++) 88 { 89 int z=lca(d[j],d[l]); 90 ans[z]+=dep[d[j]]+dep[d[l]]-2*dep[z]; 91 } 92 }else 93 { 94 dfs(1,i,0); 95 } 96 } 97 flow(1,0); 98 for (int i=1;i<=n;i++) printf("%lld ",ans[i]); 99 }

訓練結果還不錯,除了知識盲區CD,其他題都寫出來了。(E棄,畢竟純模擬