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
1⩽m⩽M求出
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,m⩽105,∣E∣⩽2∗105
博主太傻逼了,无法理解这题的做法,想了下,补充一个没卵用的所谓的严谨的说法
解法:对于
1
⩽
w
<
30
1\leqslant w <30
1⩽w<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+1⩾fai,j−>first⩾i
读者可以停下思考一下这个联通块的生成树张啥样
我们从
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<i⩽m的,
G
i
G_i
Gi的联通性比
G
i
−
1
G_{i-1}
Gi−1的更强
定义
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
j−fai,j−>second+n
所谓联通性更强,就是在
G
i
−
1
G_{i-1}
Gi−1里联通的,在
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
1⩽i⩽m第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
*/