[CF855G]Harry Vs Voldemort

[CF855G]Harry Vs Voldemort

题目大意:

一棵\(n(n\le10^5)\)个结点的树,\(q(q\le10^5)\)次操作,每次增加一条新边。每次操作后,你需要统计形如\((u,v,w)\)的三元组的数量,使得\(u,v,w\)都不相同,并存在两条分别\(u\)\(w\)\(v\)\(w\)的路径,使得两条路径没有共同边。

思路:

每次加边相当于将两个顶点之间的所有边缩成了一个边双连通分量。

考虑三元组\((u,v,w)\)

  1. \(u,v,w\)均在同一个边双中;
  2. \(u,v\)中有一个在与\(w\)相同的边双中;
  3. \(u,v,w\)均在不同的边双中。

对三种情况分别讨论即可。

对于情况2,3,只需维护边双大小\(size[u]\);对于情况1,还需维护子树大小\(tsize[u]\),和\(u,v\)分布在相同(或不同)子树中的方案数。

边双缩点可以用并查集实现。

时间复杂度\(\mathcal O(n\alpha(n))\)

源代码:

#include<cstdio>
#include<cctype>
#include<vector>
#include<numeric>
inline int getint() {
	register char ch;
	while(!isdigit(ch=getchar()));
	register int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x;
}
typedef long long int64;
const int N=1e5+1;
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
	e[u].push_back(v);
	e[v].push_back(u);
}
int64 ans,val[N],tmp[N];
int n,par[N],dep[N],size[N],tsize[N];
struct DisjointSet {
	int anc[N];
	void reset(const int &n) {
		std::iota(&anc[1],&anc[n]+1,1);
	}
	int find(const int &x) {
		return x==anc[x]?x:anc[x]=find(anc[x]);
	}
	void merge(const int &x,const int &y) {
		anc[find(x)]=find(y);
	}
	bool same(const int &x,const int &y) {
		return find(x)==find(y);
	}
};
DisjointSet djs;
void dfs(const int &x,const int &par) {
	::par[x]=par;
	dep[x]=dep[par]+1;
	size[x]=tsize[x]=1;
	for(int y:e[x]) {
		if(y==par) continue;
		dfs(y,x);
		tsize[x]+=tsize[y];
	}
}
int64 calc(const int &x) {
	int64 ans=0;
	ans+=1ll*size[x]*(size[x]-1)*(size[x]-2);//XXX
	ans+=2ll*size[x]*(size[x]-1)*(n-size[x]);//XX-Y & Y-XX
	ans+=1ll*(n-size[x])*(n-size[x])*size[x];
	for(int y:e[x]) {//Y-X-Z & Z-X-Y
		if(djs.same(x,y)) continue;
		const int z=djs.find(y);
		const int sz=y==par[x]?n-tsize[x]:tsize[z];
		ans-=1ll*sz*sz*size[x];
		tmp[x]+=1ll*sz*sz;
	}
	return val[x]=ans;
}
int64 calc2(const int &x) {
	int64 ans=0;
	ans+=1ll*size[x]*(size[x]-1)*(size[x]-2);//XXX
	ans+=2ll*size[x]*(size[x]-1)*(n-size[x]);//XX-Y & Y-XX
	ans+=1ll*(n-size[x])*(n-size[x])*size[x];//Y-X-Z & Z-X-Y
	ans-=tmp[x]*size[x];
	return val[x]=ans;
}
void merge(int u,int v) {
	u=djs.find(u);
	v=djs.find(v);
	while(u!=v) {
		if(dep[u]<dep[v]) std::swap(u,v);
		const int w=djs.find(par[u]);
		tmp[w]-=1ll*tsize[u]*tsize[u];
		tmp[w]+=tmp[u]-1ll*(n-tsize[u])*(n-tsize[u]);
		ans-=val[u];
		size[w]+=size[u];
		djs.merge(u,w);
		u=w;
	}
	ans-=val[u];
	ans+=calc2(u);
}
int main() {
	n=getint();
	for(register int i=1;i<n;i++) {
		add_edge(getint(),getint());
	}
	djs.reset(n);
	dfs(1,0);
	for(register int x=1;x<=n;x++) {
		ans+=calc(x);
	}
	printf("%lld\n",ans);
	const int q=getint();
	for(register int i=0;i<q;i++) {
		merge(getint(),getint());
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2019-05-22 09:34  skylee03  阅读(133)  评论(0编辑  收藏