P3206 [HNOI2010]城市建设

P3206 [HNOI2010]城市建设

题目描述

无向图上修改边权,动态维护 M S T MST MST,求每次修改后的MST的权值和。

Solution

有一个简单好想的做法—— L C T LCT LCT+线段树分治。
考虑每次加边,若形成了一个环,则把环上最大的一条边删掉, L C T LCT LCT维护。
然后删边就套一个线段树分治,删边操作就变成了加边和回退操作,直接做就行了。
时间复杂度:大常数 O ( n l g 2 n ) O(nlg^2n) O(nlg2n),似乎卡不过。

还有一个神奇的做法。
因为一次修改操作只会改动MST上的一条边,所以我们考虑通过线段树分治,分治区间为 [ l , r ] [l,r] [l,r],将点数和边数都保持在 O ( r − l ) O(r-l) O(rl)级别。

[ l , r ] [l,r] [l,r]中的操作涉及的边集为 S S S,当前点集为 V V V,当前边集(不包含 S S S中的边)为 E E E
有两个显然的引理:
1.把 S S S的边权值都设为 I N F INF INF,将其设为 S ′ S' S,在 ( V , E + S ′ ) (V,E+S') (V,E+S) M S T MST MST,显然若此时不在 M S T MST MST中的边在之后的分治过程中都不可能在 M S T MST MST中了,直接在 E E E删去即可。
2.把 S S S的边权值都设为 − I N F -INF INF,将其设为 S ′ ′ S'' S,在 ( V , E + S ′ ′ ) (V,E+S'') (V,E+S) M S T MST MST,显然若此时在 M S T MST MST中的边在之后的分治过程中都一定在 M S T MST MST中了,直接在 E E E删去,并加上贡献。

通过这两个引理就可以让 ∣ E ∣ , ∣ V ∣ = O ( r − l ) |E|,|V|=O(r-l) E,V=O(rl)
时间复杂度 T ( n ) = T ( n / 2 ) + O ( n l g n ) = O ( n l g 2 n ) T(n)=T(n/2)+O(nlgn)=O(nlg^2n) T(n)=T(n/2)+O(nlgn)=O(nlg2n),常数比较小。

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>

#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second

using namespace std;

template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }

typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;

const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=50005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
	int f=1,x=0; char c=getchar();
	while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
	while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
	return x*f;
}
PR a[MAXN];
int flag[MAXN],Flag[MAXN],fa[MAXN],vn[18],en[18],V[18][MAXN],To[MAXN],b[MAXN];
struct enode{ int u,v,c,id; } e[MAXN],E[18][MAXN],tmp[MAXN];
inline int compareE(enode x,enode y) { return x.c<y.c; }
inline int find(int x) { return fa[x]==x?fa[x]:fa[x]=find(fa[x]); }
inline void work1(int n,int m,int l,int r,int dep)
{
	for (int i=l;i<=r;i++) flag[a[i].fi]=1;
	for (int i=1;i<=m;i++)
	{
		tmp[i]=E[dep][i];
		if (flag[E[dep][i].id]) tmp[i].c=INF;
	}
	sort(tmp+1,tmp+m+1,compareE);
	for (int i=1;i<=n;i++) fa[i]=i;
	for (int i=1;i<=m;i++)
	{
		int x=find(tmp[i].u),y=find(tmp[i].v),c=tmp[i].c;
		if (x!=y) fa[x]=y;
		else if (c!=INF) Flag[tmp[i].id]=-1;
	}
	for (int i=l;i<=r;i++) flag[a[i].fi]=0;
}
inline void work2(int n,int m,int l,int r,int dep)
{
	for (int i=l;i<=r;i++) flag[a[i].fi]=1;
	for (int i=1;i<=m;i++)
	{
		tmp[i]=E[dep][i];
		if (flag[E[dep][i].id]) tmp[i].c=-INF;
	}
	sort(tmp+1,tmp+m+1,compareE);
	for (int i=1;i<=n;i++) fa[i]=i;
	for (int i=1;i<=m;i++)
	{
		int x=find(tmp[i].u),y=find(tmp[i].v),c=tmp[i].c;
		if (x!=y) 
		{
			fa[x]=y;
			if (c!=-INF) Flag[tmp[i].id]=1;
		}
	}
	for (int i=l;i<=r;i++) flag[a[i].fi]=0;
}
inline void solve(int l,int r,int dep,ll Ans)
{
	int n=vn[dep],m=en[dep];
	if (l==r)
	{
		for (int i=1;i<=m;i++)
		{
			tmp[i]=E[dep][i];
			if (tmp[i].id==a[l].fi) tmp[i].c=a[l].se;
		}
		sort(tmp+1,tmp+m+1,compareE);
		for (int i=1;i<=n;i++) fa[i]=i;
		for (int i=1;i<=m;i++)
		{
			int x=find(tmp[i].u),y=find(tmp[i].v),c=tmp[i].c;
			if (x!=y) fa[x]=y,Ans+=c;
		}
		printf("%lld\n",Ans);
		return;
	}
	for (int i=1;i<=m;i++) Flag[E[dep][i].id]=0;
	work1(n,m,l,r,dep);
	work2(n,m,l,r,dep);
	vn[dep+1]=en[dep+1]=0;
	for (int i=1;i<=n;i++) fa[i]=i;
	for (int i=1;i<=m;i++)
		if (Flag[E[dep][i].id]==1) 
		{
			int x=find(E[dep][i].u),y=find(E[dep][i].v);
			if (x!=y) fa[x]=y;
		}
		
	for (int i=1;i<=n;i++) 
		if (fa[i]==i) V[dep+1][++vn[dep+1]]=V[dep][i],To[i]=vn[dep+1];
	for (int i=1;i<=m;i++)
		if (Flag[E[dep][i].id]==1) Ans+=E[dep][i].c;
		else if (Flag[E[dep][i].id]==0) 
		{
			int x=find(E[dep][i].u),y=find(E[dep][i].v);
			if (x!=y) E[dep+1][++en[dep+1]]=(enode){To[x],To[y],E[dep][i].c,E[dep][i].id};
		}
		
	int mid=(l+r)>>1;
	solve(l,mid,dep+1,Ans);
	for (int i=l;i<=mid;i++) b[a[i].fi]=a[i].se;
	for (int i=1;i<=en[dep+1];i++)
		if (b[E[dep+1][i].id]!=INF) E[dep+1][i].c=b[E[dep+1][i].id];
	for (int i=l;i<=mid;i++) b[a[i].fi]=INF;
	solve(mid+1,r,dep+1,Ans);
}
int main()
{
	int n=read(),m=read(),q=read();
	for (int i=1;i<=m;i++) 
	{
		int u=read(),v=read(),c=read();
		e[i]=(enode){u,v,c,i};
	}
	for (int i=1;i<=q;i++)
	{
		int x=read(),y=read();
		a[i]=MP(x,y);
	}
	vn[0]=n,en[0]=m;
	for (int i=1;i<=n;i++) V[0][i]=i;
	for (int i=1;i<=m;i++) E[0][i]=e[i],b[i]=INF;
	solve(1,q,0,0);
	return 0;
}

代码有点丑……

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值