bzoj 2001: [Hnoi2010]City 城市建设
Description
PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。
solution
正解:CDQ分治
很久以前看过题解,一直没敢写,思路就是利用分治狂加剪枝
设 solve(l,r) 表示处理第[l,r]个询问
两个剪枝:
1.删除不可能出现在生成树中的边
2.直接加上必然出现在生成树中的边,并将并查集做永久修改
剪枝1:
把要修改的边改成-inf,然后做最小生成树,保留非inf边,因为就算这些待修改的边经过改变都变得更优,这些边也可以被选到,所以是必然出现的,我们把这些非inf边加入贡献,并把对应的连缩起来
剪枝2:
把要修改的边改成 inf,然后做最小生成树,去掉不在生成树中的边,因为就算不用这些待修改的边,也不会用到,所以直接去掉即可
这样做的复杂度是对的,第一个操作把点数缩到了询问区间的长度,第二个操作把边数缩到了点数,所以每次边数随区间长度除以2
注意一些细节:比如并查集初始化时,只能改当前的生成树,避免对之前的永久化修改产生影响
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
using namespace std;
typedef long long ll;
const int N=50005,inf=2e8+41;
struct node{
int x,y,z,id;
bool operator <(const node &pr)const{return z<pr.z;}
}e[22][N],t[N],kt[N];
struct Plan{int x,y;}h[N];
int n,m,sum[41],rk[N],fa[N],res[N];ll ans[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void build(int &tot,ll &re){
for(int i=1;i<=tot;i++)fa[t[i].x]=t[i].x,fa[t[i].y]=t[i].y;
sort(t+1,t+tot+1);
int x,y,l=0;
for(int i=1;i<=tot;i++){
x=t[i].x;y=t[i].y;
if(find(x)==find(y))continue;
kt[++l]=t[i];fa[find(y)]=find(x);
}
for(int i=1;i<=l;i++)fa[kt[i].x]=kt[i].x,fa[kt[i].y]=kt[i].y;
for(int i=1;i<=l;i++)
if(kt[i].z!=-inf)
re+=kt[i].z,fa[find(kt[i].y)]=find(kt[i].x);
l=0;
for(int i=1;i<=tot;i++){
x=t[i].x;y=t[i].y;
if(find(x)==find(y))continue;
kt[++l]=t[i];rk[t[i].id]=l;
kt[l].x=find(x);
kt[l].y=find(y);
}
tot=l;for(int i=1;i<=l;i++)t[i]=kt[i];
}
inline void delet(int &tot){
for(int i=1;i<=tot;i++)fa[t[i].x]=t[i].x,fa[t[i].y]=t[i].y;
sort(t+1,t+tot+1);
int x,y,l=0;
for(int i=1;i<=tot;i++){
x=t[i].x;y=t[i].y;
if(find(x)==find(y)){
if(t[i].z==inf)kt[++l]=t[i];
continue;
}
kt[++l]=t[i];
fa[find(y)]=find(x);
}
tot=l;for(int i=1;i<=l;i++)t[i]=kt[i];
}
inline void solve(int l,int r,int d,ll re){
if(l==r)res[h[l].x]=h[l].y;
int tot=sum[d];
for(int i=1;i<=tot;i++)
t[i]=e[d][i],t[i].z=res[t[i].id],rk[t[i].id]=i;
if(l==r){
for(int i=1;i<=tot;i++)
fa[t[i].x]=t[i].x,fa[t[i].y]=t[i].y;
sort(t+1,t+tot+1);
int x,y;
for(int i=1;i<=tot;i++){
x=t[i].x;y=t[i].y;
if(find(x)==find(y))continue;
ans[l]+=t[i].z;fa[find(y)]=find(x);
}
ans[l]+=re;
return ;
}
for(int i=l;i<=r;i++)t[rk[h[i].x]].z=-inf;
build(tot,re);
for(int i=l;i<=r;i++)t[rk[h[i].x]].z=inf;
delet(tot);
sum[d+1]=tot;for(int i=1;i<=tot;i++)e[d+1][i]=t[i];
int mid=(l+r)>>1;
solve(l,mid,d+1,re);solve(mid+1,r,d+1,re);
}
void work()
{
int Q;
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e[0][i].x,&e[0][i].y,&e[0][i].z);
e[0][i].id=i;res[i]=e[0][i].z;
}
for(int i=1;i<=Q;i++)scanf("%d%d",&h[i].x,&h[i].y);
sum[0]=m;solve(1,Q,0,0);
for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]);
}
int main()
{
work();
return 0;
}