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;
}

转载于:https://www.cnblogs.com/Yuzao/p/7968065.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值