[HNOI2010] 城市建设 题解

题意

给定一个图,每次修改一条边的边权,每次修改后输出该图的最小生成树边权和,询问间不独立。

思路

在线不好做,考虑离线下来,可以使用线段树分治。

我们称在当前区间 \(\left[ l,r\right]\) 的边为动态边,不在的边为静态边

但这样每次遍历到叶子节点的时候静态边的数量都是 \(m\) 的,无法接受。

考虑在递归的过程中删去一些无用的静态边。先将所有动态边连上,然后跑一遍最小生成树,此时树上的所有静态边在接下来的子区间一定会选上,可以提前把其贡献加入答案中然后删去。此时再将连上的动态边给撤销回去,注意到未被删去的静态边中有些因树上的静态边连上了而导致无用,可以删去这些边。

遍历到叶子的时候直接暴力跑最小生成树即可。

复杂度证明,在第一次删边中把点数控制到区间长度级别,第二次删边中把边数控制为点数,所以边数是区间长度级别,所以分治复杂度带一个 \(\log\),因为要并查集维护和排序,所以总体复杂度是 \(O(n \log^2 n)\)

代码

点击查看代码
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=(a);i<=(n);++i)
#define per(i,a,n) for(int i=(n);i>=(a);--i)
#define SZ(x) ((int)(x).size())

using namespace std;
typedef double db;
typedef long long ll;
typedef pair<int,int> pii;

const int maxn=2e5+10;

int n,m,q;
struct node{
	int u,v,w;
	bool operator < (const node &A)const{
		return w<A.w;
	}
}e[maxn];
struct dsu{
    int fa[maxn],sz[maxn],n;
    stack<pii> stk;

    inline void init(int x){
        n=x;
        rep(i,1,n) fa[i]=i,sz[i]=1;
    }

    inline int find(int x){
    	while(x^fa[x]) x=fa[x];
    	return x;
    }    
    
    inline bool merge(int id){
    	int u=e[id].u,v=e[id].v;
        u=find(u); v=find(v);
        if(u^v){
        	if(sz[u]>sz[v]) swap(u,v);
        	fa[u]=v;
        	sz[v]+=sz[u];
        	stk.push({u,v});
        	return true;
    	}	
    	return false;
    }   

    inline void reback(int o){
    	while(SZ(stk)>o){
    		auto [u,v]=stk.top();
    		fa[u]=u; sz[v]-=sz[u];
    		stk.pop();
    	}
    }
}T;
pii opt[maxn];
vector<int> mov,sta;
bool vis[maxn];

inline void divide(int l,int r,ll res){
	vector<int> Mov=mov,Sta=sta,t,keep;
	int p=SZ(T.stk);
	rep(i,l,r) vis[opt[i].first]=true;
	for(auto i:mov){
		if(vis[i]) t.push_back(i);
		else sta.push_back(i);
	}
	rep(i,l,r) vis[opt[i].first]=false;
	mov=t;
	for(auto i:mov) T.merge(i);
	sort(sta.begin(),sta.end(),[&](int x,int y){return e[x]<e[y];});
	t.clear();
	for(auto i:sta){
		if(T.merge(i)){
			res+=e[i].w;
			keep.push_back(i);
		}
		else t.push_back(i);
	}
	sta=t;
	T.reback(p);
	for(auto i:keep) T.merge(i);
	int o=SZ(T.stk);
	t.clear();
	for(auto i:sta){
		if(T.merge(i))
			t.push_back(i);
	}
	sta=t;
	T.reback(o);
	if(l==r){
		e[opt[l].first].w=opt[l].second;
		sta.push_back(opt[l].first);
		sort(sta.begin(),sta.end(),[&](int x,int y){return e[x]<e[y];});
		for(auto i:sta){
			if(T.merge(i)) 
				res+=e[i].w;
		}
		printf("%lld\n",res);
	}
	else{
		int mid=(l+r)>>1;
		divide(l,mid,res); divide(mid+1,r,res);
	}
	mov=Mov; sta=Sta;
	T.reback(p);
}

inline void solve(){
	scanf("%d%d%d",&n,&m,&q);
	rep(i,1,m) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w),mov.push_back(i);
	rep(i,1,q) scanf("%d%d",&opt[i].first,&opt[i].second);	
	T.init(n);
	divide(1,q,0);
}

signed main(){
    int _=1;
    //scanf("%d",&_); 
    while(_--) solve();
}
posted @   lying_up  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示