P9194 Triples of Cows P 题解

· · 题解

删除一个点影响太大,考虑增加一些额外结点。

把第 i 条边拆为 (u,n+i),(v,n+i)

记原来的点是黑点,新加的点为白点。

不难发现新生成的图还是一棵树,且一个白点所连的所有黑点在原图两两有边。

考虑删除一个黑点 u,就是让 u 的子白点 v 合并到父亲白点 fa_u 上。

并查集维护即可。

考虑计算答案。不妨三元组 (a,b,c) 变为五元组 (a,x,b,y,c),其中 x,y 为白点,其余为黑点。那么有以下情况:

f_x 为白点 x 的黑儿子个数,随便选三个即可,贡献 (f_x+1)\times f_x \times (f_x-1)

枚举 x,y,贡献 \sum\limits_{x,y\in son_b,x\neq y} f_x\times f_y=(\sum\limits_{x} f_x)^2-\sum\limits_{x} f_x^2

贡献为 2\times f_x\sum\limits_{b\in son_x} \sum\limits_{y\in son_b} f_y,前面为选 a(除 b 以外的其他儿子和 x 的父亲)的方案,后面为选 c 的方案。

i 为黑点,定义 g_i=\sum\limits_{j\in son_i} f_j

i 为白点,定义 h_i=\sum\limits_{j\in son_i} g_i

考虑对上面的所有情况贡献求和,那么答案为:

删除一个点 $u$,受影响的有子结点 $v$,父结点 $x$,父父结点 $y$,父父父结点 $z$。 合并 $v \rightarrow x$,维护 $f,g,h$ 值即可。 尤其注意合并时要剔除 $f_v$ 对 $t_x$ 的贡献。 ```cpp #include<bits/stdc++.h> #define int long long using namespace std; const int N=4e5+5; int n,ans,f[N],g[N],h[N],fa[N],F[N]; vector<int> G[N]; int find(int x) {return x==F[x]?x:F[x]=find(F[x]);} int calc(int i) {if(!i) return 0;return (f[i]+1)*f[i]*(f[i]-1)-f[i]*f[i]+2*f[i]*h[i];} void dfs(int u) { for(int v:G[u]) { if(v==fa[u]) continue; fa[v]=u,dfs(v); if(u<=n) g[u]+=f[v]; else f[u]++,h[u]+=g[v]; } } signed main() { ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n; for(int i=1;i<n;i++) { int u,v;cin>>u>>v; G[u].push_back(n+i),G[n+i].push_back(u); G[v].push_back(n+i),G[n+i].push_back(v); } dfs(n);//以 n 为根因为 n 是最后一个删除的 for(int i=1;i<=n;i++) ans+=g[i]*g[i]; for(int i=n+1;i<2*n;i++) ans+=calc(i),F[i]=i; cout<<ans<<'\n'; for(int i=1;i<n;i++) { int u=i,x=find(fa[u]),y=fa[x],z=find(fa[y]); ans-=g[u]*g[u]+calc(x)+g[y]*g[y]+calc(z); f[x]--,g[y]--,h[z]--; for(int v:G[u]) { if(v==fa[u]) continue; F[v]=x; ans-=calc(v); f[x]+=f[v]; h[x]-=f[v],h[x]+=h[v]; g[y]+=f[v],h[z]+=f[v]; } ans+=calc(x)+g[y]*g[y]+calc(z); cout<<ans<<'\n'; } } ```