【BZOJ】2001: [Hnoi2010]City 城市建设-cdq分治/LCT

传送门:bzoj2001


题解

此题LCT动态维护MST貌似很可做(然而本蒟蒻不会)
这里分享看到的一种很妙的分治做法。

我们简化一下问题:
每次修改 ( x , y ) 权值,若能使MST总边权变小,则满足:
原MST上 x > y 路径上最大值大于 ( x , y ) 权值
(仔细想想,感觉非常有道理:
若大于等于 x > y 路径上,即使可能大于MST中最小边权,但对于更新总边权不会有任何贡献,
因为 ( x , y ) 只能替换 x > y 路径上的边。

若使MST总边权变大,这个不好操作,所以我们就预处理后倒着做(离线操作)。
我们cdq的时候,要不断缩小当前需要操作MST的边集,可以每次分治调用一下两个操作。

若当前范围为 L ~ R

  • 1: R e d u c t i o n
    把修改L-R所影响到的所有边的边权设为INF,然后跑MST,则此时都选不上的不在修改边集里的边直接舍去,因为不管怎样这些边都不会被选中,假设现在还没有没舍去的边有cnt条,操作的时候减一减就好了。

  • 2: C o n t r a c t i o n :
    把修改L-R所影响到的所有边的边权设为-INF,然后跑MST,则此时都选得上的不在修改边集里的边直接选中,因为不管怎样这些边都会被选中,在下一层分治操作中就不用管这些边了,我们假设所有锁定边为must,往下传就好了。

c d q   p r o c e s s :

Solve(l,r,G);G当前选中的边的集合(上层分治Reduction舍去了一些边 Contraction 选定了一些边,所以不为全集)
Reduction(G);
Contraction(G);
Solve(l,mid,G);
solve(mid+1,r,G);
Recover(G);

ps:
后Contraction,先Reduction,这样跑Contraction时就可以少跑一些边了。
其实无所谓。

边界:l==r G中只有一条边,直接改了加,as[i]存第i个操作的答案。

R-C(n) MST时间复杂度 (nlogn)(nlogn的Kruskal的排序,本蒟蒻之前还以为是prim的一些操作orzzzz)
Recover 差不多
总共(n log n^2)-> (2nlogn)


代码

#include<bits/stdc++.h>
#define inf 0x7fffffff
using namespace std;
typedef long long ll;
const int N=5e4+10;

int n,m,Q,cur,f[N],tag[N];
ll as[N],a[N];
struct L{int u,v,id;ll d;}d[N],t[N][20],tp[N];
struct P{int k;ll d;}c[N];
inline int find(int x){return f[x]==0? x:f[x]=find(f[x]);}
inline bool cmp(const L& a,const L& b){return a.d<b.d;}

inline int rd()
{
    char ch=getchar();int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}

inline void itia(int cnt)
{for(int i=1;i<=cnt;i++) f[d[i].v]=f[d[i].u]=0;}

inline void Reduction(int &cnt)
{
    cur=0;itia(cnt);
    sort(d+1,d+cnt+1,cmp);
    for(int du,dv,i=1;i<=cnt;i++){
        du=find(d[i].u);dv=find(d[i].v);
        if(du!=dv){
            f[du]=dv;
            tp[++cur]=d[i];tag[d[i].id]=cur;
        }else if(d[i].d==inf) tp[++cur]=d[i],tag[d[i].id]=cur;
    }
    cnt=cur;
    for(int i=1;i<=cnt;i++) d[i]=tp[i];
}

inline void Contraction(int &cnt,ll &must)
{
    cur=0;itia(cnt);
    sort(d+1,d+cnt+1,cmp);
    for(int du,dv,i=1;i<=cnt;i++){
        du=find(d[i].u);dv=find(d[i].v);
        if(du!=dv) f[du]=dv,tp[++cur]=d[i];
    }
    for(int i=1;i<=cur;i++) f[tp[i].u]=f[tp[i].v]=0;
    for(int du,dv,i=1;i<=cur;i++){
        du=find(tp[i].u);dv=find(tp[i].v);
        if(tp[i].d!=-inf && du!=dv) f[du]=dv,must+=tp[i].d;
    }cur=0;
    for(int i=1;i<=cnt;i++){
        if(find(d[i].u)!=find(d[i].v))  {
        tp[++cur]=d[i];tag[d[i].id]=cur;
        tp[cur].u=find(d[i].u),tp[cur].v=find(d[i].v);
        }
    }
    cnt=cur;
    for(int i=1;i<=cnt;i++) d[i]=tp[i];
}

inline void cdq(int l,int r,int depth,int cnt,ll must)
{
    if(l==r) a[c[l].k]=c[l].d;
    for(int i=1;i<=cnt;i++) t[i][depth].d=a[t[i][depth].id];
    for(int i=1;i<=cnt;i++) 
      d[i]=t[i][depth],tag[d[i].id]=i;
    //printf("\npay");
    //for(int i=1;i<=cnt;i++) printf("%d ",a[t[i][depth].id]);
    if(l==r){
        as[l]=must;itia(cnt);
        sort(d+1,d+cnt+1,cmp);
        for(int du,dv,i=1;i<=cnt;i++){
            du=find(d[i].u);dv=find(d[i].v);
            if(du!=dv) f[du]=dv,as[l]+=d[i].d;
        }return;
    }
    for(int i=l;i<=r;i++) d[tag[c[i].k]].d=inf;
    Reduction(cnt);
    for(int i=l;i<=r;i++) d[tag[c[i].k]].d=-inf;
    Contraction(cnt,must);
    for(int i=1;i<=cnt;i++) t[i][depth+1]=d[i];
    int mid=(l+r)>>1;
    cdq(l,mid,depth+1,cnt,must);cdq(mid+1,r,depth+1,cnt,must);
}

int main(){
    n=rd();m=rd();Q=rd();
    for(int i=1;i<=m;i++) t[i][0].id=i,t[i][0].u=rd(),t[i][0].v=rd(),t[i][0].d=a[i]=rd();
    for(int i=1;i<=Q;i++) c[i].k=rd(),c[i].d=rd();
    cdq(1,Q,0,m,0);
    for(int i=1;i<=Q;i++) printf("%lld\n",as[i]);
    return 0;
}

跑得慢怪我咯QAQ

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/80330025