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弃,毕竟纯模拟

posted @ 2021-07-05 17:44  praying_cqf  阅读(61)  评论(0)    收藏  举报