CF1648E 题解

发布时间 2023-08-16 16:42:59作者: Albertvαn

就是 \(m\) 组询问补图的最小生成树上的树链最大值。有两种基本思路求这棵树。

第一种,Kruskal,基于找到最小的边使两端点不连通。考虑补图中 \((x,y)\) 的边权,它是原图最小生成树上的树链最大值。从小到大枚举补图的边,相当于从小到大枚举原图最小生成树的边 \((u,v,w)\),然后:

令原图上,连接这条边之前,\(u\) 所在连通块为 \(S\)\(v\) 所在连通块为 \(T\),那么对于 \(x\in S,y\in T\),必然有补图中 \((x,y)\) 的边权恰为 \(w\)。当然这建立在补图中 \((x,y)\) 这条边存在的前提下。

所以直接模拟这个过程,暴力枚举 \(x\) 所在补图连通块 \(X\)(显然 \(X\subseteq S\)),然后不断地去寻找 \(y\) 所在补图连通块 \(Y(Y\subseteq T)\)。为了判断 \(X\)\(Y\) 是否可以合并,枚举 \(x\in X\)\(y\in T\),观察 \((x,y)\) 这条边在原图是否不存在即可。

四重循环。这个复杂度分三部分。其一,合并 \(X\)\(Y\)。启发式合并即可总计 \(\mathcal O(n\log n)\)

其二,寻找 \(Y\) 的过程中枚举 \(y\)。注意到每对 \((x,y)\) 至多枚举一次,\(y\) 会被跳过当且仅当原图存在边 \((x,y)\)。所以总共 \(\mathcal O(m)\)

其三,合并 \(X\)\(Y\) 的次数。显然是 \(\mathcal O(n)\)

然后注意到判断 \((x,y)\) 原图是否有边是 \(\Theta(\log m)\) 的。于是令 \(m=\Omega(n)\) 则总复杂度 \(\mathcal O(m\log m)\)。这个应该是 CF 的 std。


第二种,Boruvka,基于对每个点找到离开所在连通块的最小边。对原图建出 Kruskal 重构树,补图中 \((x,y)\) 边权为 \(w_{\operatorname {lca(x,y)}}\)

问题转化为,对于所有叶子结点 \(u\),找到其最深的祖先 \(a\),满足 \(a\) 的子树内存在一个 \(v\) 使得:

  • \(v\) 不在 \(u\) 的连通块内;

  • 原图中 \((u,v)\) 无边。

考虑连通块在重构树上形如一段 dfn 区间。合并连通块就是合并区间,同时子树也是一段 dfn 区间,于是二分一下即可找到 \(a\)

对于原图无边这个限制,枚举 \(u\) 的所有 \(k\) 个出点,然后把查询区间暴力分成 \(\mathcal O(k)\) 个子段。这样一次 Boruvka 的过程只会查 \(\mathcal O(m)\) 个区间。

总复杂度 \(\mathcal O(n\log^2 n)\)

个人写的是第一种思路。有点难调。

#include <cstdio>
#include <vector>
#include <algorithm>
#include <set>
#include <cstring>

using namespace std;

namespace fIO{
	char c;void re(int &x){
		x=0;c=getchar();
		while(c<'0'||c>'9') c=getchar();
		while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	}
	char st[20];int tp;
	void wr(int n){
		while(n) st[++tp]=n%10+48,n/=10;
		while(tp) putchar(st[tp--]);
	}
}using fIO::re;using fIO::wr;

const int N=231231;

struct U{
	int anc[N];
	void set(int n){for(int i=1;i<=n;++i) anc[i]=i;}
	int qry(int x){return anc[x]==x?x:anc[x]=qry(anc[x]);}
	bool sam(int x,int y){return qry(x)==qry(y);}
	void mrg(int x,int y){anc[qry(x)]=qry(y);}
}O,B;

namespace AT{
	struct edg{int v,w;};
	vector<edg> vc[N];
	void lnk(int x,int y,int w){
		vc[x].push_back((edg){y,w});vc[y].push_back((edg){x,w});
	}
	int f[N][18],anc[N][18],dep[N];
	void build(int now,int lst){
		anc[now][0]=lst;dep[now]=dep[lst]+1;
		for(int i=1;(1<<i)<dep[now];++i) anc[now][i]=anc[anc[now][i-1]][i-1],
			f[now][i]=max(f[now][i-1],f[anc[now][i-1]][i-1]);
		for(auto[v,w]:vc[now]) if(v!=lst) f[v][0]=w,build(v,now);
	}
	int qry(int x,int y){
		int res=0;if(dep[x]<dep[y]) swap(x,y);
		for(int i=17;~i;--i) if((1<<i)<=dep[x]-dep[y])
			res=max(res,f[x][i]),x=anc[x][i];
		for(int i=17;~i;--i) if(anc[x][i]!=anc[y][i])
			res=max(res,max(f[x][i],f[y][i])),x=anc[x][i],y=anc[y][i];
		if(x!=y) res=max(res,max(f[x][0],f[y][0]));
		return res;
	}
	void clr(int n){
		for(int i=1;i<=n;++i) vc[i].clear(),memset(anc[i],0,sizeof(anc[i]));
	}
}

set<int> blk[N],sub[N];

int merg(int X,int Y){
	if(blk[X].size()>blk[Y].size()) swap(X,Y);
	for(int x:blk[X]) blk[Y].insert(x);
	return Y;
}

vector<int> vc[N],del;int pos[N];

void mrgsub(int &u,int &v,int w){
	if(sub[B.qry(u)].size()>sub[B.qry(v)].size()) swap(u,v);
	int pu=u,pv=v;u=B.qry(u);v=B.qry(v);
	for(int X:sub[u]){
		del.clear();int p=X;
		for(int Y:sub[v]){
			for(int x:blk[X]){
				bool flg=0;for(int y:blk[Y]){
					auto it=lower_bound(vc[x].begin(),vc[x].end(),y);
					if(it==vc[x].end()||*it!=y){
					flg=1;p=merg(p,Y);del.push_back(Y);AT::lnk(x,y,w);break;
				}}
				if(flg) break;
			}
		}
		for(int Y:del) sub[v].erase(Y);
		sub[v].insert(p);
	}
	B.mrg(u,v);u=pu;v=pv;
}

struct edg{int x,y,w,id;}e[N];

int ans[N];

int main()
{
	int T;re(T);while(T--){
		int n,m;re(n);re(m);O.set(n);B.set(n);AT::clr(n);
		for(int i=1;i<=n;++i) pos[i]=i,vc[i].clear(),
			sub[i].clear(),sub[i].insert(i),blk[i].clear(),blk[i].insert(i);
		for(int i=1;i<=m;++i) re(e[i].x),re(e[i].y),re(e[i].w),e[i].id=i,
			vc[e[i].x].push_back(e[i].y),vc[e[i].y].push_back(e[i].x);
		for(int i=1;i<=n;++i) sort(vc[i].begin(),vc[i].end());
		sort(e+1,e+m+1,[](edg u,edg v){return u.w<v.w;});
		for(int i=1;i<=m;++i) if(!O.sam(e[i].x,e[i].y))
			mrgsub(e[i].x,e[i].y,e[i].w),O.mrg(e[i].x,e[i].y);
		AT::build(1,0);
		for(int i=1;i<=m;++i) ans[e[i].id]=AT::qry(e[i].x,e[i].y);
		for(int i=1;i<=m;++i) wr(ans[i]),putchar(' ');puts("");
	}
}