您的位置:首页 > 其它

城市建设

2016-02-23 22:34 204 查看

城市建设

时间限制: 2 Sec 内存限制: 128 MB

题目描述

PS国是一个拥有诸多城市的大国,国王Louis为城市间的道路修建可谓绞尽脑汁。Louis可以在某些城市之间修建道路,并且在不同城市之间修建道路需要不同的花费。Louis希望修建最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随时改变,Louis会不断收到道路修建花费被改变的消息,他希望每收到一条消息后能立即知道使城市连通的最小总花费,Louis决定求助于你来完成这个任务。

输入

从文件input.txt中读入数据,输入文件第一行是用空格隔开的三个整数N,M,Q,分别表示城市的数目,可以修建的道路的条数,以及收到的消息的个数。接下来有M行,第i+1行是用空格隔开的三个整数Xi,Yi,Zi(1≤Xi,Yi≤N,0≤Zi≤50000000),表示在城市Xi与城市Yi之间修建道路的花费为Zi。紧接着的Q行,每行是用空格隔开的两个整数Kj和Dj,表示前面输入的第Kj条道路的修建花费被修改为Dj。输入的数据保证20%的数据满足N≤1000,M≤6000,Q≤6000。20%的数据满足N≤1000,M≤50000,Q≤8000且修改后的花费不会比之前的花费低。100%的数据满足N≤20000,M≤50000,Q≤50000。

输出

输出文件output.txt包含Q行,第i行输出收到前i条消息后使城市连通的最小总花费。

样例输入

5 5 3

1 2 1

2 3 2

3 4 3

4 5 4

5 1 5

1 6

1 1

5 3

样例输出

14

10

9

来源

HNOI2010

题解

带修改边权维护mst。

先来证明一些引理。

work_1:考虑将所有询问点所代表的边赋为-inf,此时仍在mst上的边就一定是mst上的边。可以将这些边缩起来。

work_2:考虑将所有询问点所代表的边赋为inf,此时不在mst上的非询问边一定不在mst上,可以将这些边删去。

可以证明,这样操作后剩余的点数和边数都可以减少到O(询问数)级别。

所以可以考虑通过对询问分治的手法来减少边数和点数。

对于一段询问区间,有些边可以保证一定在mst上,有些边则能保证不在mst上,我们可以将这些边合并,从而缩小边和点的范围。

对于合并操作,可以用并查集来维护。

注意边和询问的存储和备份等细节。

另外,这种做法可以支持完全动态维护mst,即带加边,删边,修改边权。对于不在原图中的边,把初始边权赋为inf即可。

其实我很想知道这题在线怎么写

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define inf 1050000000
#define N 20010
#define M 50010
using namespace std;
int n,m,q,fa
,pos[M];
int val[M],flag[M],new1
,new2[M],tot,cnt;
ll ans[M];
struct node{int a,b,c;}map[M],edge[M*20];
struct info{int a,b;}t[M],ask[N*20];
bool cmp(const int &x,const int &y)
{return val[x]<val[y];}

int find(int x)
{
if(!fa[x])return x;
return fa[x]=find(fa[x]);
}

bool merge(int a,int b)
{
a=find(a);b=find(b);
if(a==b)return false;
fa[b]=a;return true;
}

void solve(int n,node *map,int m,int l,int r,ll sum)
{
int cnt1=0,cnt2=0;
if(l==r)
{
for(int i=1;i<=m;i++)pos[i]=i,val[i]=map[i].c;
for(int i=1;i<=n;i++)fa[i]=0;
val[t[l].a]=t[l].b;
sort(pos+1,pos+m+1,cmp);
for(int i=1;i<=m;i++)
{
int e=pos[i];
int a=find(map[e].a),b=find(map[e].b);
if(a!=b)fa[b]=a,sum+=val[e];
}
ans[l]=sum;return;
}
for(int i=1;i<=n;i++)new1[i]=0;
for(int i=1;i<=m;i++)new2[i]=0;
//work_1
for(int i=1;i<=n;i++)fa[i]=0;
for(int i=1;i<=m;i++)pos[i]=i,flag[i]=0,val[i]=map[i].c;
for(int i=l;i<=r;i++)val[t[i].a]=-inf;
sort(pos+1,pos+m+1,cmp);
for(int i=1;i<=m;i++)
{
int e=pos[i];
int a=find(map[e].a),b=find(map[e].b);
if(a==b)continue;fa[b]=a;
if(val[e]>-inf)sum+=val[e],flag[e]=1;
}
for(int i=1;i<=n;i++)fa[i]=0;
for(int i=1;i<=m;i++)
if(flag[i])merge(map[i].a,map[i].b);

for(int i=1;i<=n;i++)if(find(i)==i)new1[i]=++cnt1;
for(int i=1;i<=n;i++)if(find(i)!=i)new1[i]=new1[find(i)];
for(int i=1;i<=m;i++)
map[i].a=new1[map[i].a],map[i].b=new1[map[i].b];
//work_2
for(int i=1;i<=n;i++)fa[i]=0;
for(int i=1;i<=m;i++)pos[i]=i,flag[i]=0;
for(int i=l;i<=r;i++)val[t[i].a]=inf;
sort(pos+1,pos+m+1,cmp);
for(int i=1;i<=m;i++)
{
int e=pos[i];
int a=find(map[e].a),b=find(map[e].b);
if(a!=b)fa[b]=a;
if(a!=b||val[e]==inf)flag[e]=1;
}
for(int i=1;i<=m;i++)if(flag[i])new2[i]=++cnt2;
for(int i=l;i<=r;i++)t[i].a=new2[t[i].a];
for(int i=1;i<=m;i++)if(flag[i])map[new2[i]]=map[i];
//pushdown
int mid=l+r>>1;
for(int i=1;i<=cnt2;i++)edge[++tot]=map[i];
for(int i=l;i<=r;i++)ask[++cnt]=t[i];
if(l<=mid)solve(cnt1,map,cnt2,l,mid,sum);
for(int i=cnt2;i;i--)map[i]=edge[tot--];
for(int i=r;i>=l;i--)t[i]=ask[cnt--];
for(int i=l;i<=mid;i++)map[t[i].a].c=t[i].b;
if(r>mid)solve(cnt1,map,cnt2,mid+1,r,sum);
}

int main()
{
int a,b,c;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a,&b,&c),map[i]=(node){a,b,c};
for(int i=1;i<=q;i++)
scanf("%d%d",&a,&b),t[i]=(info){a,b};
solve(n,map,m,1,q,0);
for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: