您的位置:首页 > 其它

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

2017-07-11 22:53 411 查看

题意

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|≤R−L+1。

缩图有这样两个核心操作:

Reduction:

把修改边的边权暂且记为 INF ,对全图刷 MST ,不在这个 MST 中的边,不管之后修改的边权为多少,一定是没用的,直接扔掉。

显然用这个操作可以使边数不超过 |S|+n−1 。

Contraction:

把修改边的边权暂且记为 −INF ,对全图刷 MST,在这个MST中的非修改边,不管之后修改的边权为多少,都是必须要选的。

把这些边连上后,把一块连通的点缩在一起。

这个操作可以使点数不超过 n−((n−1)−|S|)=|S|+1 。

上述两个操作的复杂度都为O(mlogm)。

所以我们执行 Contraction−Reduction,图的规模就从 (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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: