[图的联通性]2018ecfinal A

wxh的题解
b站
博主太鸽子了,于是打算水一篇题解
题意:给定二分图G,左右各n个点,边权范围为[1,30]。定义 G m G^m Gm为二分图重复m次,有m+1层点,第i层和第i+1层之间的连边同G。对于 1 ⩽ m ⩽ M 1\leqslant m \leqslant M 1mM求出 G m G^m Gm的最小生成树
n , m ⩽ 1 0 5 , ∣ E ∣ ⩽ 2 ∗ 1 0 5 n,m\leqslant 10^5,|E|\leqslant 2*10^5 n,m105,E2105
博主太傻逼了,无法理解这题的做法,想了下,补充一个没卵用的所谓的严谨的说法
解法:对于 1 ⩽ w < 30 1\leqslant w <30 1w<30的每个w求出 ⩽ w \leqslant w w的边形成了多少个联通块
假设我们处理好了 G m G^m Gm的联通情况
不妨假设我们将这个联通情况的每个联通块弄出了一个树,然后给每个点指定fa(联通块生成树的根不用指定fa)
具体而言的,我们会对每个点 ( i , j ) (i,j) (i,j)设立 f a i , j fa_{i,j} fai,j,表示第i层第j个点的父亲是啥(同样用一个二元组表示,如果没有父亲就表示为 ( − 1 , − 1 ) (-1,-1) (1,1))
对于 f a i , j − > f i r s t ≠ − 1 fa_{i,j}->first\neq -1 fai,j>first=1的, i + 1 ⩾ f a i , j − > f i r s t ⩾ i i+1\geqslant fa_{i,j}->first \geqslant i i+1fai,j>firsti
读者可以停下思考一下这个联通块的生成树张啥样
我们从 G m G^m Gm扩充到 G m + 1 G^{m+1} Gm+1
m+1可以视为前m个G和后m个G的并
但如果把 G m G^m Gm中两层之间的边都右移一次,从而实现联通性的保证,处理起来边太多了,没有头绪
于是我们提出一个归纳假设:
对于 1 < i ⩽ m 1<i\leqslant m 1<im的, G i G_i Gi的联通性比 G i − 1 G_{i-1} Gi1的更强
定义 G i G_i Gi: 2 × n 2\times n 2×n个点,对于 f a i , j − > f i r s t ! = − 1 fa_{i,j}->first!=-1 fai,j>first!=1的,连接 j − f a i , j − > s e c o n d + n j-fa_{i,j}->second+n jfai,j>second+n
所谓联通性更强,就是在 G i − 1 G_{i-1} Gi1里联通的,在 G i G_i Gi里也是联通的
于是只用把 f a m , f a m + 1 fa_m,fa_{m+1} fam,fam+1的边暴躁复制到 m + 1 , m + 2 m+1,m+2 m+1,m+2层即可
梳理一下,对于 1 ⩽ i ⩽ m 1\leqslant i \leqslant m 1im第i层我们都是 f a i fa_i fai的连边
m + 1 m+1 m+1层是 f a m , f a m + 1 fa_m,fa_{m+1} fam,fam+1的连边
m + 2 m+2 m+2层是 f a m + 1 fa_{m+1} fam+1的连边
于是要做好m+1和m+2两层连边的维护,得到 f a m ′ , f a m + 1 ′ fa'_m,fa'_{m+1} fam,fam+1,保证好每个联通块恰有一棵生成树
至于如何得到 f a m ′ , f a m + 1 ′ fa'_m,fa'_{m+1} fam,fam+1的边,上面的题解,包括下面的代码都表示出来了
很显然,上面联通性更强的归纳仍然成立

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,M,m;
#define Maxn 100010
ll Ans[Maxn];
ll lastsum[Maxn];

struct Edge{int s,e;};
vector<Edge> edge[31];
vector<Edge> cur,nex;

int fa[Maxn<<1];
int find(int x){
	if(fa[x]!=x)fa[x]=find(fa[x]);
	return fa[x];
}

inline void solve(int x){
	cur.clear();
	ll res=0;
	for(int i=1;i<=2*n;++i)fa[i]=i;
	for(int i=0;i<edge[x].size();++i){
		int fx=find(edge[x][i].s);
		int fy=find(edge[x][i].e+n);
		if(fx==fy)continue;
		if(fx>n&&fy>n)cur.push_back((Edge){fx-n,fy-n});
		if(fx>fy)swap(fx,fy);
		fa[fx]=fy;
	}
	int siz1=0;
	for(register int i=1;i<=n;++i)
		if(find(i)==i)siz1++;
	int siz2=0;
	for(register int i=n+1;i<=2*n;++i)
		if(find(i)==i)siz2++;
	res+=siz1;
	Ans[1]+=1ll*(2*n-siz1-siz2-lastsum[1])*x;
	lastsum[1]=2*n-siz1-siz2;
	for(register int i=2;i<=M;++i,cur=nex,nex.clear()){
		for(int j=0;j<cur.size();++j){
			int fx=find(cur[j].s);
			int fy=find(cur[j].e);
			if(fx==fy)continue;
			if(fx>fy)swap(fx,fy);
			fa[fx]=fy;
			if(fx>n&&fy>n)nex.push_back((Edge){fx-n,fy-n}),siz2--;
			else siz1--;
		}
		res+=siz1;
		Ans[i]+=1ll*(1ll*n*(i+1)-res-siz2-lastsum[i])*x;
		lastsum[i]=1ll*n*(i+1)-res-siz2;
	}
}

inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}

int main(){
	rd(n);rd(M);rd(m);
	int s,e,t;
	for(register int i=1;i<=m;++i){
		rd(s);rd(e);rd(t);
		for(register int j=t;j<=30;++j)edge[j].push_back((Edge){s,e});
	}
    for(register int i=1;i<=30;++i)solve(i);
    for(register int i=1;i<=M;++i)printf("%lld\n",Ans[i]);
    return 0;
}/*
4 4 8
3 4 12
1 1 20
1 3 22
4 2 12
4 4 2
2 2 2
1 2 2
1 4 2
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值