[HNOI2010]CITY城市建设

本文介绍了一种处理动态修改边权的最小生成树(MST)问题的算法。通过结合线段树分治和LCT技术,在每次修改时通过极端情况(负无穷和正无穷)来缩小图的规模,并利用分治策略进行高效处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

动态改边权MSTMST


题解

考虑和线段树分治+LCT+LCT一样的思想,对修改时间分治

考虑一段时间的修改的那些边

暴力就是当L=RL=R时直接让修改生效,,然后求一遍MST

这样是不行的因为图的规模没有还是mm

所以在每一层分治中就是考虑怎么缩小图的规模

考虑一个修改的两种极端情况

1.把这条边变成−∞

我们把这个分治区间[L,R][L,R]要修改的边边权全部赋值为,−∞,然后求一遍MSTMST

那么可以发现,,当前还在MST中的非−∞边在以后也一定会在MSTMST

所以我们考虑缩点,把这些边连成的联通块缩成一个点,,然后把答案加上

这样是会形成一个森林的,所以我们要把连接森林的边存下来继续分治

2.2.把这条边变成

我们把这个分治区间[L,R][L,R]要修改的边边权全部赋值为,∞,然后求一遍MSTMST

那么可以发现,,当前不在MST中的边在之后的分治过程中一定是不需要了的

所以我们只要保存那些MSTMST中的边和权值为的边,然后继续分治

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
    while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
char sr[1<<21],z[20];int C=-1,Z;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
template<class T>inline void we(T x){
    if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=5e4+5,inf=1e9;
typedef int arr[N];
typedef long long ll;
struct mdy{int x,y;}p[N];
struct eg{
    int u,v,w,id;
    inline bool operator<(const eg b)const{return w<b.w;}
}e[18][N],d[N],t[N];
int n,m,q,Sum[18];arr a,c,fa,sz;ll ans[N];
int gf(int x){return fa[x]==x?x:fa[x]=gf(fa[x]);}
inline void link(int u,int v){
    u=gf(u),v=gf(v);if(sz[u]>sz[v])swap(u,v);
    fa[u]=v;sz[v]+=sz[u];
}
inline void clr(int T,eg*a){
    fp(i,1,T){
        fa[a[i].u]=a[i].u;
        fa[a[i].v]=a[i].v;
        sz[a[i].u]=sz[a[i].v]=1;
    }
}
inline void contraction(int&T,ll&val){
    int tp=0;
    clr(T,d);sort(d+1,d+T+1);
    fp(i,1,T)if(gf(d[i].u)^gf(d[i].v))
        link(d[i].u,d[i].v),t[++tp]=d[i];
    clr(tp,t);
    fp(i,1,tp)if(t[i].w!=-inf&&gf(t[i].u)^gf(t[i].v))
        val+=t[i].w,link(t[i].u,t[i].v);
    tp=0;
    fp(i,1,T)if(gf(d[i].u)^gf(d[i].v))
        t[++tp]={fa[d[i].u],fa[d[i].v],d[i].w,d[i].id};
    fp(i,1,tp)c[d[i].id]=i,d[i]=t[i];T=tp;
}
inline void reduction(int&T){
    int tp=0;
    clr(T,d);sort(d+1,d+T+1);
    fp(i,1,T)
        if(gf(d[i].u)^gf(d[i].v))
            link(d[i].u,d[i].v),t[++tp]=d[i];
        else if(d[i].w==inf)t[++tp]=d[i];
    fp(i,1,tp)c[d[i].id]=i,d[i]=t[i];T=tp;
}
void cdq(int L,int R,int dep,ll val){
    int T=Sum[dep];
    if(L==R)a[p[L].x]=p[L].y;
    fp(i,1,T){
        e[dep][i].w=a[e[dep][i].id];
        d[i]=e[dep][i],c[d[i].id]=i;
    }
    if(L==R){
        ans[L]=val;clr(T,d);
        sort(d+1,d+T+1);
        fp(i,1,T)if(gf(d[i].u)^gf(d[i].v))
            link(d[i].u,d[i].v),ans[L]+=d[i].w;
        return;
    }
    fp(i,L,R)d[c[p[i].x]].w=-inf;
    contraction(T,val);
    fp(i,L,R)d[c[p[i].x]].w=inf;
    reduction(T);
    fp(i,1,T)e[dep+1][i]=d[i];
    Sum[dep+1]=T;int mid=(L+R)>>1;
    cdq(L,mid,dep+1,val);
    cdq(mid+1,R,dep+1,val);
}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    sd(n),sd(m),sd(q);
    fp(i,1,m){int u,v;
        sd(u),sd(v),sd(a[i]);
        e[0][i]={u,v,a[i],i};
    }
    fp(i,1,q)sd(p[i].x),sd(p[i].y);
    Sum[0]=m;cdq(1,q,0,0);
    fp(i,1,q)we(ans[i]);
return Ot(),0;
}

这题用线段树分治竟然没卡过看来窝常数还是太大了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值