题解:洛谷 P9984([USACO23DEC] A Graph Problem P)
1. Description
给出一张连通的无向图,包含编号为 \(1\dots N\) 的节点和编号为 \(1\dots M\)(\(2 \le N \le 2\cdot 10^5\),\(N - 1 \le M \le 4 \cdot 10^5\))的边,下边的操作将被实施:
- 假设集合 \(S=\{v\}\),变量 \(h=0\)。
- 当 \(|S|<N\),重复执行:
- 仅有一个顶点在集合 \(S\) 中的边中,找到编号最小的那条,编号记为 \(e\)。
- 将 \(e\) 不在 \(S\) 中的那个顶点加入集合 \(S\)。
- 将 \(h\) 修改为 \(10h+e\)。
- 返回 \(h\) 对 \(10^9+7\) 取模的值。
输出这个过程的全部返回值。
2.Solution
给出一种只需使用启发式合并的并查集,而无需路径压缩的解法。
我们发现这个过程相当于以 \(v\) 为初始节点,生成一棵最小生成树,因此考虑以边的编号为边权,建出 Kruskal 重构树,然后在构造 Kruskal 重构树的过程中求解答案。
考虑一条边 \(e\),它将连接 \(u,v\) 所在的连通块,此时考虑 \(u\) 所属联通块中的点答案如何修改。
显然,对于 \(u\) 所属连通块中的一个点的访问顺序,有以下结论:
- 连接后原来 \(u\) 所属连通块中的边仍然会被优先访问,且访问顺序不变。
- 在 \(u\) 所属连通块中的边被遍历完之后,必然优先访问 \(e\)。
- 访问 \(e\) 之后,将访问 \(v\) 所属联通块中的所有边,且访问顺序和 \(v\) 一致。
以上结论皆由边编号的大小关系得出,读者可以自己思考原因。
所以,对于 \(u\) 所属联通块中的一个点 \(x\),它的答案将从 \(ans_x\) 变为 \((ans_x\times 10+e)\times 10^{cnt_v}+ans_v\),其中 \(cnt_v\) 表示 \(v\) 这一联通块中的边数。
拆开括号,则有 \(ans_x\) 变为 \(ans_x\times 10^{cnt_v+1}+e\times 10^{cnt_v}+ans_v\),也就是对于 \(u\) 所属联通块中的答案,先乘上 \(10^{cnt_v+1}\) 后加上 \(e\times 10^{cnt_v}+ans_v\)。
对于 \(v\) 所属的联通块中的点同理,先乘上 \(10^{cnt_u+1}\) 后加上 \(e\times 10^{cnt_u}+ans_u\)。
这里可以用并查集维护连通块,同时使用永久化标记 \(add,mul\) 维护修改,当我们合并两个连通块 \(u,v\) 时,我们需要维护 \(add_v,mul_v\),保证修改后的 \(add^{\prime}_v,mul^{\prime}_v\) 满足:
解方程,有 \(mul^{\prime}_v=\frac{mul_v}{mul_u},add^{\prime}_v=\frac{add_v-add_u}{mul_u}\)。
通过以上维护,虽然是构建 Kruskal 重构树时求解,但是此时无需加入新的节点,也无需保证父子关系,可以直接使用启发式合并并查集维护连通块并进行修改。
最后 \(i\) 的答案就是以 \(i\) 为起点,到根的路径上的点的标记对初始值为 \(0\) 的变量依次进行修改的结果。
3. Code
/*by qwer6*/
/*略去缺省源与快读快写*/
const int N=2e5+5,mod=1e9+7;
int n,m;
int pw[N];
int Add(int x,int y){
x+=y;
return x>=mod?x-mod:x;
}
int Mul(int x,int y){
long long res=1ll*x*y;
return res>=mod?res%mod:res;
}
int Sub(int x,int y){
x-=y;
return x<0?x+mod:x;
}
int binpow(int a,int b){
int res=1;
while(b){
if(b&1)res=Mul(res,a);
a=Mul(a,a);
b>>=1;
}
return res;
}
int Inv(int x){
return binpow(x,mod-2);
}
struct DSU{
int n;
int fa[N],siz[N],add[N],mul[N];
void init(int _n=0){
n=_n;
for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1,add[i]=0,mul[i]=1;
}
void Tag(int x,int v1,int v2){
add[x]=Add(Mul(add[x],v1),v2);
mul[x]=Mul(mul[x],v1);
}
int find(int x){
if(x==fa[x])return x;
return find(fa[x]);
}
void merge(int x,int y){
x=find(x),y=find(y);
if(x==y)return ;
if(siz[x]<siz[y])swap(x,y);
int inv=Inv(mul[x]);
mul[y]=Mul(mul[y],inv);
add[y]=Mul(Sub(add[y],add[x]),inv);
fa[y]=x;
siz[x]+=siz[y];
return ;
}
int cal(int x,int &ans){
ans=Add(Mul(ans,mul[x]),add[x]);
if(x==fa[x])return x;
return cal(fa[x],ans);
}
}dsu;
signed main(){
read(n),read(m);
pw[0]=1;
for(int i=1;i<=n;i++)pw[i]=Mul(pw[i-1],10);
dsu.init(n);
for(int i=1,u,v,valu,valv;i<=m;i++){
read(u),read(v);
valu=valv=0;
u=dsu.cal(u,valu),v=dsu.cal(v,valv);
if(u==v)continue;
dsu.Tag(u,pw[dsu.siz[v]],Add(Mul(i,pw[dsu.siz[v]-1]),valv));
dsu.Tag(v,pw[dsu.siz[u]],Add(Mul(i,pw[dsu.siz[u]-1]),valu));
dsu.merge(u,v);
}
for(int i=1,ans;i<=n;i++){
ans=0;
dsu.cal(i,ans);
write(ans),Nxt;
}
}

浙公网安备 33010602011771号