P9984 [USACO23DEC] A Graph Problem P
题意
给出一张图,求每个点开始每一次走编号最小另一个点没有加入的边的边的编号拼起来的数字。
\(n\leq 2\cdot 10^5\)
思路
我们考虑最小生成树,那么考虑一条边的一个节点,那么一定是先走完它的已经经过的集合再走这条边再走另一个点的集合。那么我们可以用 Kruskal 重构树去维护,等价于区间乘和加。但是我们发现,我们可以直接在并查集上打区间乘和加的标记,因为是支持路径压缩的。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long ll;
const int MAXN=2e5+5;
const int MOD=1e9+7;
int n,m,f[MAXN*2];
pair<int,int>ans[MAXN*2];
pair<int,int>merge(pair<int,int>x,pair<int,int>y){
return {1ll*x.first*y.first%MOD,(1ll*x.second*y.first%MOD+y.second)%MOD};
}
int find(int u){
if(u==f[u]){
return u;
}
int fa=find(f[u]);
ans[u]=merge(ans[u],ans[f[u]]);
f[u]=fa;
// cout<<u<<" "<<fa<<" "<<ans[u].second<<" "<<ans[fa].second<<"\n";
return fa;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
rep(i,1,2*n){
f[i]=i;
ans[i]={1,0};
}
int tot=n;
rep(i,1,m){
int x,y;
cin>>x>>y;
int rx=find(x),ry=find(y);
if(rx==ry){
continue;
}
pair<int,int>vx=ans[x],vy=ans[y];
ans[rx]=merge({10,i},vy);
ans[ry]=merge({10,i},vx);
// cout<<x<<" "<<y<<" "<<ans[rx].second<<" "<<ans[ry].second<<" "<<rx<<" "<<ry<<"\n";
f[rx]=f[ry]=++tot;
}
rep(i,1,n){
find(i);
cout<<ans[i].second<<"\n";
}
return 0;
}

浙公网安备 33010602011771号