#分治,Kruskal#洛谷 3206 [HNOI2010]城市建设

题目

动态改边权求最小生成树

\(n\leq 2*10^4,m\leq 5*10^4,q\leq 5*10^4\)


分析

乍一看是线段树分治,但有一个很大的问题就是局部的Kruskal不一定是最后的选择,

但是如果在底部再处理所有边的话,那仍然是 \(O(qm\log m)\) 的复杂度,必须在到达底层之前处理掉一些边。

由于所有边只是边权改变,并不需要线段树分治(常数会更大)。

分治到 \([l,r]\) 区间时,考虑两种特殊情况,就是需要修改的边无限大或者无限小。

无限小的时候,如果其它边被选上了,那么它们在之后肯定也会被选上,

直接缩成同一个连通块并统计答案,这样就保证点数一定与 \(r-l\) 同级

无限大的时候,如果没有被选上,一定不会被选上,直接不管这些边,

这样在无限小之后保证边数与点数同级。

各跑一次Kruskal,时间复杂度 \(O(m\log q\log m)\)


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=50011,inf=1e9; typedef long long lll;
struct rec{int x,w;}q[N];
struct node{int x,y,w,rk;}e[21][N],E[N];
int edge[21],w[N],rk[N],f[N],b[N],n,m,Q; lll ans[21];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void print(lll ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
bool cmp(node x,node y){return x.w<y.w;}
int getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
void dfs(int dep,int l,int r){
	int Edge=edge[dep]; lll Ans=ans[dep];
	if (l==r) w[q[l].x]=q[l].w;
	for (int i=1;i<=Edge;++i){
		e[dep][i].w=w[e[dep][i].rk];
		E[i]=e[dep][i],rk[E[i].rk]=i;
	}
	if (l==r){
		sort(E+1,E+1+Edge,cmp);
		for (int i=1;i<=Edge;++i)
			f[E[i].x]=E[i].x,f[E[i].y]=E[i].y;
		for (int i=1;i<=Edge;++i){
			int fa=getf(E[i].x),fb=getf(E[i].y);
			if (fa>fb) fa^=fb,fb^=fa,fa^=fb; 
			if (fa!=fb) Ans+=E[i].w,f[fa]=fb;
		}
		print(Ans),putchar(10);
		return;
	}
	for (int i=l;i<=r;++i) E[rk[q[i].x]].w=-inf;
	for (int i=1;i<=Edge;++i) f[E[i].x]=E[i].x,f[E[i].y]=E[i].y;
	sort(E+1,E+1+Edge,cmp);
	int EDGE=0;
	for (int i=1;i<=Edge;++i){
		int fa=getf(E[i].x),fb=getf(E[i].y);
		if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
		if (fa!=fb) f[fa]=fb,b[++EDGE]=i;
	}
	for (int i=1;i<=EDGE;++i) f[E[b[i]].x]=E[b[i]].x,f[E[b[i]].y]=E[b[i]].y;
	for (int i=1;i<=EDGE;++i)
	if (E[b[i]].w!=-inf){
		int fa=getf(E[b[i]].x),fb=getf(E[b[i]].y);
		if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
		Ans+=E[b[i]].w,f[fa]=fb;
	}
	EDGE=0;
	for (int i=1;i<=Edge;++i){
		int fa=getf(E[i].x),fb=getf(E[i].y);
		if (fa!=fb) b[++EDGE]=i;
	}
	for (int i=1;i<=EDGE;++i){
		E[i]=(node){f[E[b[i]].x],f[E[b[i]].y],E[b[i]].w,E[b[i]].rk};
		rk[E[i].rk]=i;
	}
	Edge=EDGE;
	for (int i=l;i<=r;++i) E[rk[q[i].x]].w=inf;//无限小时大于r的rk被放在最后即使被删除也不会影响
	for (int i=1;i<=Edge;++i) f[E[i].x]=E[i].x,f[E[i].y]=E[i].y;
	sort(E+1,E+1+Edge,cmp),EDGE=0;
	for (int i=1;i<=Edge;++i){
		int fa=getf(E[i].x),fb=getf(E[i].y);
		if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
		if (fa!=fb) f[fa]=fb,b[++EDGE]=i;
		    else if (E[i].w==inf) b[++EDGE]=i;
	}
	for (int i=1;i<=EDGE;++i){
		E[i]=(node){E[b[i]].x,E[b[i]].y,E[b[i]].w,E[b[i]].rk};
		rk[E[i].rk]=i;
	}
	Edge=EDGE;
	for (int i=1;i<=Edge;++i) e[dep+1][i]=E[i];
	int mid=(l+r)>>1;
	edge[dep+1]=Edge,ans[dep+1]=Ans;
	dfs(dep+1,l,mid),dfs(dep+1,mid+1,r);
}
int main(){
	n=iut(),m=iut(),Q=iut();
	for (int i=1;i<=m;++i)
	    e[0][i]=(node){iut(),iut(),w[i]=iut(),i};
	for (int i=1;i<=Q;++i) q[i]=(rec){iut(),iut()};
	edge[0]=m,dfs(0,1,Q);
	return 0;
}
posted @ 2022-02-17 20:33  lemondinosaur  阅读(22)  评论(0编辑  收藏  举报