【Luogu】 P3206 [HNOI2010] 城市建设

题目链接

点击打开链接

题目解法

动态 m s t mst mst 板板题~

考虑类似于线段树分治的做法
我们需要把边划分成静态边和动态边
动态边是当前分治区间 [ l , r ] [l,r] [l,r] 中修改的边,其他边是静态边
我们考虑到静态边的边集太大,考虑缩小范围,不难想到 答案加上必选边 和 删去无用边

  1. 令动态边的边权为 − ∞ -\infty ,这样仍在 m s t mst mst 中的边就是必选边(在区间 [ l , r ] [l,r] [l,r] 中的任何一个子区间都是)
  2. 令动态边的边权为 + ∞ +\infty +,这样不在 m s t mst mst 中的边就是无用边(在区间 [ l , r ] [l,r] [l,r] 中的任何一个子区间都是)

对于必选边,我们需要把它们缩点
然后不要忘记把除了删边其他的 u , v u,v u,v 全部改成缩点之后的集合
我们提前加上必选边的贡献,且删去无用边之后,不难发现,静态边的个数是 O ( r − l + 1 ) O(r-l+1) O(rl+1) 级别的
且有一个非常重要的性质是,我们在分治树上一层一层往下递归时,静态边是递减的,这样可以缩小问题规模
最后当 l = r l=r l=r 时,做一遍 m s t mst mst 即可
时间复杂度 O ( q l o g m ) O(qlogm) O(qlogm)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=20100,M=50100,inf=1e9;
struct Lines{ int x,y,z,id;}e[20][M],f[M],tmp[M],t[M];
struct Updt{ int x,d;}upd[M];
int n,m,q,c[M],fa[N],rv[M],totE[20];
LL ans[M];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void _clear(int curE){ for(int i=1;i<=curE;i++) fa[f[i].x]=f[i].x,fa[f[i].y]=f[i].y;}
void _sort(int curE){ sort(f+1,f+curE+1,[](const Lines &i,const Lines &j){ return i.z<j.z;});}
int get_father(int x){ return x==fa[x]?x:fa[x]=get_father(fa[x]);}
void slv1(int &curE,LL &mst){
	_sort(curE),_clear(curE);
	int cnt=0;
	for(int i=1;i<=curE;i++){
		int fax=get_father(f[i].x),fay=get_father(f[i].y);
		if(fax!=fay) fa[fax]=fay,t[++cnt]=f[i];
	}
    _clear(curE);
    for(int i=1;i<=cnt;i++) if(t[i].z!=-inf) mst+=t[i].z,fa[get_father(t[i].x)]=get_father(t[i].y);
    int sz=0;
    for(int i=1;i<=curE;i++){
        int fax=get_father(f[i].x),fay=get_father(f[i].y);
        if(fax!=fay) f[i].x=fax,f[i].y=fay,tmp[++sz]=f[i];
    }
	for(int i=1;i<=sz;i++) f[i]=tmp[i],rv[f[i].id]=i;
    curE=sz;
}
void slv2(int &curE){
	_sort(curE),_clear(curE);
	int sz=0;
	for(int i=1;i<=curE;i++){
		int fax=get_father(f[i].x),fay=get_father(f[i].y);
		if(fax!=fay) fa[fax]=fay,tmp[++sz]=f[i];
		else if(f[i].z==inf) tmp[++sz]=f[i];
	}
	for(int i=1;i<=sz;i++) f[i]=tmp[i],rv[f[i].id]=i;
    curE=sz;
}
void solve(int l,int r,int depth,LL mst){
	int curE=totE[depth];
	if(l==r) c[upd[l].x]=upd[l].d;
	for(int i=1;i<=curE;i++){
		e[depth][i].z=c[e[depth][i].id];
		f[i]=e[depth][i],rv[f[i].id]=i;
	}
	if(l==r){
		ans[l]=mst;
		_sort(curE),_clear(curE);
		for(int i=1;i<=curE;i++){
			int fax=get_father(f[i].x),fay=get_father(f[i].y);
			if(fax!=fay) fa[fax]=fay,ans[l]+=f[i].z;
		}
		return;
	}
	for(int i=l;i<=r;i++) f[rv[upd[i].x]].z=-inf;
	slv1(curE,mst);
	for(int i=l;i<=r;i++) f[rv[upd[i].x]].z=inf;
	slv2(curE);
	for(int i=1;i<=curE;i++) e[depth+1][i]=f[i];
	totE[depth+1]=curE;
	int mid=(l+r)>>1;
	solve(l,mid,depth+1,mst),solve(mid+1,r,depth+1,mst);
}
int main(){
	n=read(),m=read(),q=read();
	for(int i=1;i<=m;i++){
		int x=read(),y=read(),z=read();
		e[0][i]={x,y,z,i},c[i]=z;
	}
	for(int i=1;i<=q;i++) upd[i].x=read(),upd[i].d=read();
	totE[0]=m,solve(1,q,0,0);
	for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
	fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值