城市建设

城市建设

时间限制: 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[N],pos[M];
int val[M],flag[M],new1[N],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;
}

你可能感兴趣的:(城市建设)