[动态MST] [CDQ分治] BZOJ2001: [Hnoi2010]City 城市建设

来源:互联网 发布:sql语法基础知识 编辑:程序博客网 时间:2024/06/02 04:01

题意

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

题解

这个题目是每次修改边权,需要你马上求出全局的MST。
改变一条边后对MST的影响是很大的,显然要往CDQ分治的方面想。
一般的CDQ分治都是考虑左边的修改对右边询问的贡献,但是这道题的这个贡献很难简单表达。
所以这里用了一个很特殊的思路:分治时不断重建图使得图的规模变小,保证复杂度。
大概就是:

void Solve(L,R){    if(L==R){ 直接计算ans[L]; return; }    干一些奥妙重重的事情缩图;    Solve(L,mid); Solve(mid+1,R);}

为了方便表述,对于 [L,R] 所有操作修改到的边,我们称作修改边,修改边的集合为 S|S|RL+1
缩图有这样两个核心操作:
Reduction:
把修改边的边权暂且记为 INF ,对全图刷 MST ,不在这个 MST 中的边,不管之后修改的边权为多少,一定是没用的,直接扔掉。
显然用这个操作可以使边数不超过 |S|+n1
Contraction:
把修改边的边权暂且记为 INF ,对全图刷 MST,在这个MST中的非修改边,不管之后修改的边权为多少,都是必须要选的。
把这些边连上后,把一块连通的点缩在一起。
这个操作可以使点数不超过 n((n1)|S|)=|S|+1

上述两个操作的复杂度都为O(mlogm)
所以我们执行 ContractionReduction,图的规模就从 (n,m) 变成 (|S|+1,2|S|) 了!
然后就好了,复杂度为O(mlog2m)

有点难写,于是参考了网上很多dalao的代码,后来改来改去都写的几乎一样了…

#include<cstdio>#include<algorithm>#define Fir first #define Sec second using namespace std;typedef long long LL;const int maxn=20005,maxq=50005,maxe=50005;int w[maxe];LL ans[maxq];struct Edge{    int x,y,w,id;    bool operator < (const Edge &B)const{        return w<B.w;    }} e[25][maxe],tmp[maxe],t_e[maxe];pair<int,int> q[maxq];int fa[maxn],t_id[maxn];bool vis[maxn];int getfa(int x){ fa[x]=fa[x]==x?x:fa[x]=getfa(fa[x]); }void merge(int x,int y){    x=getfa(x); y=getfa(y);    if(x!=y) fa[x]=y;}void Contraction(int &_n,int &_m,LL &res){    int n_now=0,m_now=0;    for(int i=1;i<=_n;i++) fa[i]=i;     sort(tmp+1,tmp+1+_m);    for(int i=1;i<=_m;i++){        if(getfa(tmp[i].x)!=getfa(tmp[i].y))            merge(tmp[i].x,tmp[i].y), t_e[++m_now]=tmp[i];    }    for(int i=1;i<=_n;i++) fa[i]=i;    for(int i=1;i<=m_now;i++)      if(t_e[i].w!=-1e+9&&getfa(t_e[i].x)!=getfa(t_e[i].y))      merge(t_e[i].x,t_e[i].y), res+=t_e[i].w;    for(int i=1;i<=_n;i++) vis[i]=false;    for(int i=1;i<=_n;i++) if(!vis[getfa(i)]) vis[getfa(i)]=true, t_id[getfa(i)]=++n_now;    m_now=0;    for(int i=1;i<=_m;i++)        if(getfa(tmp[i].x)!=getfa(tmp[i].y)){            t_e[++m_now]=tmp[i];            t_e[m_now].x=t_id[getfa(tmp[i].x)];            t_e[m_now].y=t_id[getfa(tmp[i].y)];        }    for(int i=1;i<=m_now;i++) tmp[i]=t_e[i];    _n=n_now; _m=m_now;}void Reduction(int _n,int &_m){    for(int i=1;i<=_n;i++) fa[i]=i;     sort(tmp+1,tmp+1+_m);    int m_now=0;    for(int i=1;i<=_m;i++){        if(getfa(tmp[i].x)!=getfa(tmp[i].y)){            merge(tmp[i].x,tmp[i].y);            t_e[++m_now]=tmp[i];        } else if(tmp[i].w==1e+9) t_e[++m_now]=tmp[i];    }    for(int i=1;i<=m_now;i++) tmp[i]=t_e[i];    _m=m_now;}int pos[maxe];void Solve(int L,int R,int k,int _n,int _m,LL res){    if(L==R) w[q[L].Fir]=q[L].Sec;    for(int i=1;i<=_m;i++) e[k][i].w=w[e[k][i].id];    for(int i=1;i<=_m;i++) tmp[i]=e[k][i];    if(L==R){        for(int i=1;i<=_n;i++) fa[i]=i;        sort(tmp+1,tmp+1+_m);        for(int i=1;i<=_m;i++) if(getfa(tmp[i].x)!=getfa(tmp[i].y)){            merge(tmp[i].x,tmp[i].y); res+=tmp[i].w;        }        ans[L]=res;        return;    }    for(int i=1;i<=_m;i++) pos[tmp[i].id]=i;    for(int i=L;i<=R;i++) tmp[pos[q[i].Fir]].w=-1e+9;    Contraction(_n,_m,res);    for(int i=1;i<=_m;i++) pos[tmp[i].id]=i;    for(int i=L;i<=R;i++) tmp[pos[q[i].Fir]].w=1e+9;    Reduction(_n,_m);    for(int i=1;i<=_m;i++) e[k+1][i]=tmp[i];     int mid=(L+R)>>1;    Solve(L,mid,k+1,_n,_m,res); Solve(mid+1,R,k+1,_n,_m,res);}int n,m,Q;int main(){    freopen("bzoj2001.in","r",stdin);    freopen("bzoj2001.out","w",stdout);    scanf("%d%d%d",&n,&m,&Q);    for(int i=1;i<=m;i++){        scanf("%d%d%d",&e[0][i].x,&e[0][i].y,&e[0][i].w);        w[e[0][i].id=i]=e[0][i].w;    }    for(int i=1;i<=Q;i++) scanf("%d%d",&q[i].Fir,&q[i].Sec);    Solve(1,Q,0,n,m,0);    for(int i=1;i<=Q;i++) printf("%lld\n",ans[i]);    return 0;} 
阅读全文
0 0
原创粉丝点击