Processing math: 100%

[CF855G]Harry Vs Voldemort

[CF855G]Harry Vs Voldemort

题目大意:

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

思路:

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

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

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

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

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

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

时间复杂度O(nα(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 @   skylee03  阅读(267)  评论(0编辑  收藏  举报
编辑推荐:
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
阅读排行:
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· 软件产品开发中常见的10个问题及处理方法
· Vite CVE-2025-30208 安全漏洞
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· MQ 如何保证数据一致性?
历史上的今天:
2018-05-22 [CF607D]Power Tree
2018-05-22 [CF864F]Cities Excursions
2017-05-22 [USACO07NOV]牛栏Cow Hurdles
2017-05-22 [USACO09JAN]最好的地方Best Spot
点击右上角即可分享
微信分享提示