【题目】
原题地址
定义三元组
(
u
,
v
,
w
)
(u,v,w)
(u,v,w)为合法的当且仅当存在路径
u
→
w
,
v
→
w
u\rightarrow w,v\rightarrow w
u→w,v→w且两条路径没有边交。初始给定一棵
n
n
n个点的树,
q
q
q次操作每次增加一条边,并询问当前图中有多少个合法三元组。
n
,
q
≤
1
0
5
n,q\leq 10^5
n,q≤105
【解题思路】
如果只是一棵树的话,可以通过枚举中间点
w
w
w来计数。
如果是一个图的话,若
u
,
v
,
w
u,v,w
u,v,w存在于同一个边双中,则三元组
(
u
,
v
,
w
)
(u,v,w)
(u,v,w)必然满足条件。
对于不在同一个边双中的合法三元组,分两种情况,一种是中间点和一个端点在同一个边双中,这时另一个端点可以随便取;一种是三个点都不在同一个边双中,这样的话就跟一般的树是一样的。 于是
只要维护每个点作为
w
w
w的贡献,我们用
DSU
\text{DSU}
DSU来维护边双,同时维护答案即可。
更具体地,考虑怎样维护答案,假设我们现在合并了 S , T S,T S,T两个集合,那么显然要先减去由这两个集合中的点作为 w w w的贡献,有三种情况:
- u , v u,v u,v均为 w w w所在边双中的点
- u , v u,v u,v其中一个在 w w w所在边双内,令一个不在
-
u
,
v
u,v
u,v均不在
w
w
w所在边双中
其中前两个我们可以简单用边双大小来维护。
第三个直接统计并不是很方便,于是我们容斥一下,计算来自边双同一侧的 u , v u,v u,v有多少对,这个可以通过维护 f u f_u fu表示 u u u连通块中一个点能匹配到同侧的两个点有多少种方案,记 s u s_u su为 u u u连通块大小,则不合法方案为 w u × f u w_u\times f_u wu×fu。合并 w w w时,令 u u u为 v v v的父亲,如果我们直接 w u + w v w_u+w_v wu+wv显然会将 v v v所在子树内的点对以及不在 v v v子树中,但在 u u u子树中的点计算两次,这里再减掉贡献就可以了。
复杂度 O ( n α ( n ) ) O(n\alpha(n)) O(nα(n))
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e5+10;
ll Q,n,tot,head[N];
ll dep[N],siz[N],sz[N];
ll f[N],fa[N],w[N];
ll ans;
ll read()
{
ll ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(ll x){if(x>9) write((ll)x/10);putchar((x%10)^48);}
void writeln(ll x){write(x);puts("");}
struct Tway{ll v,nex;}e[N<<1];
void add(ll u,ll v)
{
e[++tot]=(Tway){v,head[u]};head[u]=tot;
e[++tot]=(Tway){u,head[v]};head[v]=tot;
}
ll sqr(ll x){return (ll)x*x;}
void dfs(ll x)
{
siz[x]=1;dep[x]=dep[fa[x]]+1;
for(ll i=head[x];i;i=e[i].nex)
{
ll v=e[i].v;
if(v==fa[x]) continue;
fa[v]=x;dfs(v);siz[x]+=siz[v];w[x]+=sqr(siz[v]);
}
ans-=(ll)(w[x]+=sqr(n-siz[x]));
}
ll findf(ll x){return f[x]==x?x:f[x]=findf(f[x]);}
ll get(ll x){return (ll)sz[x]*(sz[x]-1)*(sz[x]-2);}
void merge(ll x,ll y)
{
ans-=(sqr(n-sz[x])-w[x])*sz[x];
ans-=(sqr(n-sz[y])-w[y])*sz[y];
//cerr<<ans<<endl;
ans-=(n-sz[x])*sz[x]*(sz[x]-1)*2;
ans-=(n-sz[y])*sz[y]*(sz[y]-1)*2;
ans-=get(x)+get(y);f[y]=x;sz[x]+=sz[y];
//cerr<<ans<<endl;
w[x]+=w[y]-sqr(siz[y])-sqr(n-siz[y]);
//cerr<<ans<<endl;
ans+=(sqr(n-sz[x])-w[x])*sz[x]+get(x);
//cerr<<ans<<" "<<sz[x]<<endl;
ans+=(n-sz[x])*sz[x]*(sz[x]-1)*2;
//cerr<<ans<<endl;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CF855G.in","r",stdin);
freopen("CF855G.out","w",stdout);
#endif
n=read();
for(ll i=1;i<n;++i) add(read(),read());
ans=n*(n-1)*(n-1);dfs(1);
for(ll i=1;i<=n;++i) f[i]=i,sz[i]=1;
Q=read();writeln(ans);
while(Q--)
{
ll u=findf(read()),v=findf(read());
while(u^v)
{
if(dep[u]<dep[v]) swap(u,v);
ll t=findf(fa[u]);merge(t,u);u=t;
}
writeln(ans);
}
return 0;
}