[动态最小生成树 CDQ分治 Kruscal] BZOJ 2001 [Hnoi2010]City 城市建设

30 篇文章 0 订阅
11 篇文章 0 订阅

思路不难想 借鉴了其他人的代码:http://blog.sina.com.cn/s/blog_6e63f59e0101blum.html

两个关键的操作:
Reduction(删除无用边):
把待修改的边标为INF,做一遍MST,把做完后不在MST中的非INF边删去(因为这些边在原图的情况下肯定更不可能选进MST的边集,即无用边);
Contraction(缩必须边,缩点):
把待修改的边标为-INF,做一遍MST,在MST中的非-INF边为必须边(因为这些边在原图的情况下也一定会被选进MST),缩点。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef pair<int,int> abcd;
typedef long long ll;

inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(int &x)
{
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int oo=1<<30;
const int N=50005;

int n,m;

struct edge{
	int u,v,w,pos;
	bool operator < (const edge &B) const{
		return w<B.w;
	}
}e[25][N],d[N],t[N];
int c[N];
int a[N];

int fat[N],rank[N];

inline void init(edge *d,int n){
	for (int i=1;i<=n;i++)
		fat[d[i].u]=d[i].u,fat[d[i].v]=d[i].v,rank[d[i].u]=rank[d[i].v]=0;
}

inline int Fat(int u){
	return u==fat[u]?u:fat[u]=Fat(fat[u]);
}

inline bool Union(int x,int y){
	x=Fat(x),y=Fat(y); if (x==y) return 0;
	if (rank[x]>rank[y]) swap(x,y);
	if (rank[x]==rank[y]) rank[y]++;
	fat[x]=y; return 1;
}

abcd q[N]; int Q;
ll ans[N];

inline ll contraction(int &eds){
	int tmp=0; ll tsum=0;
	init(d,eds);
	sort(d+1,d+eds+1);
	for (int i=1;i<=eds;i++)
		if (Union(d[i].u,d[i].v))
			t[++tmp]=d[i];
	init(d,eds);
	for (int i=1;i<=tmp;i++)
		if (t[i].w!=-oo && Union(t[i].u,t[i].v))
			tsum+=t[i].w;
	tmp=0;
	for (int i=1;i<=eds;i++)
		if (Fat(d[i].u)!=Fat(d[i].v))
		{
			t[++tmp]=d[i];
			t[tmp].u=Fat(t[tmp].u);
			t[tmp].v=Fat(t[tmp].v);
			c[t[tmp].pos]=tmp;
		}
	for (int i=1;i<=tmp;i++) d[i]=t[i]; eds=tmp;
	return tsum;
}

inline void reduce(int &eds)
{
	int tmp=0;
	init(d,eds);
	sort(d+1,d+eds+1);
	for (int i=1;i<=eds;i++)
		if (Union(d[i].u,d[i].v) || d[i].w==oo){
			t[++tmp]=d[i];
			c[t[tmp].pos]=tmp;
		}
	for (int i=1;i<=tmp;i++) d[i]=t[i]; eds=tmp;
}

void Solve(int l,int r,int cur,int eds,ll sum)
{
	if (l==r) a[q[l].first]=q[l].second;
	for (int i=1;i<=eds;i++) 
		e[cur][i].w=a[e[cur][i].pos],d[i]=e[cur][i],c[d[i].pos]=i;
	if (l==r){
		ans[l]=sum;
		init(d,eds);
		sort(d+1,d+eds+1);
		for (int i=1;i<=eds;i++)
			if (Union(d[i].u,d[i].v))
				ans[l]+=d[i].w;
		return;		
	}
	for (int i=l;i<=r;i++) d[c[q[i].first]].w=-oo;
	sum+=contraction(eds);
	for (int i=l;i<=r;i++) d[c[q[i].first]].w=oo;
	reduce(eds);
	for (int i=1;i<=eds;i++) e[cur+1][i]=d[i];
	int mid=(l+r)>>1;
	Solve(l,mid,cur+1,eds,sum);
	Solve(mid+1,r,cur+1,eds,sum);
}

int main()
{
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	read(n); read(m); read(Q);
	for (int i=1;i<=m;i++)
		read(e[0][i].u),read(e[0][i].v),read(e[0][i].w),a[i]=e[0][i].w,e[0][i].pos=i;
	for (int i=1;i<=Q;i++)
		read(q[i].first),read(q[i].second);
	Solve(1,Q,0,m,0);
	for (int i=1;i<=Q;i++)
		printf("%lld\n",ans[i]);
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值