P3206 [HNOI2010] 城市建设

题意

给一张 \(n\) 个点,\(m\) 条边的唔无向图,有边权,\(q\) 次修改,每次修改一条边的边权,查询最小生成树上的边权和。
\(n\le2\times 10^4\)\(m,q\le5\times10^4\)

思路

把修改边权看作删除原来的边,加入一条新的边。记录每条边出现的时间段,问题变成:给一张图,每次加入/删除一条边,查询最小生成树的边权和。用线段树分治 \(+LCT\) 维护。
时间复杂度 \(\mathcal O(q\log n\log m)\)

代码

要注意常数,否则容易 \(TLE\)

// Problem: P3206 [HNOI2010] 城市建设
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3206
// Memory Limit: 500 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace ly{
    namespace IO{
        #ifndef LOCAL
            constexpr auto maxn=1<<20;
            char in[maxn],out[maxn],*p1=in,*p2=in,*p3=out;
            #define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,maxn,stdin),p1==p2)?EOF:*p1++)
            #define flush() (fwrite(out,1,p3-out,stdout))
            #define putchar(x) (p3==out+maxn&&(flush(),p3=out),*p3++=(x))
            class Flush{public:~Flush(){flush();}}_;
        #endif
        namespace usr{
            template<typename type>
            inline type read(type &x){
                x=0;bool flag(0);char ch=getchar();
                while(!isdigit(ch)) flag^=ch=='-',ch=getchar();
                while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
                return flag?x=-x:x;
            }
            template<typename type>
            inline void write(type x){
                x<0?x=-x,putchar('-'):0;
                static short Stack[50],top(0);
                do Stack[++top]=x%10,x/=10;while(x);
                while(top) putchar(Stack[top--]|48);
            }
            inline char read(char &x){do x=getchar();while(isspace(x));return x;}
            inline char write(const char &x){return putchar(x);}
            inline void read(char *x){static char ch;read(ch);do *(x++)=ch;while(!isspace(ch=getchar())&&~ch);}
            template<typename type>inline void write(type *x){while(*x)putchar(*(x++));}
            inline void read(string &x){static char ch;read(ch),x.clear();do x+=ch;while(!isspace(ch=getchar())&&~ch);}
            inline void write(const string &x){for(int i=0,len=x.length();i<len;++i)putchar(x[i]);}
            template<typename type,typename...T>inline void read(type &x,T&...y){read(x),read(y...);}
            template<typename type,typename...T>
            inline void write(const type &x,const T&...y){write(x),putchar(' '),write(y...),sizeof...(y)^1?0:putchar('\n');}
            template<typename type>
            inline void put(const type &x,bool flag=1){write(x),flag?putchar('\n'):putchar(' ');}
        }
        #ifndef LOCAL
            #undef getchar
            #undef flush
            #undef putchar
        #endif
    }using namespace IO::usr;
}using namespace ly::IO::usr;
#define LL long long
const int maxn=50010,maxtime=50010,maxm=100000,maxq=500010;
int n,m,q;
class Link_Cut_Tree{
private:
    struct Link_Cut_Tree_node{
        int z,ch[2],fa,maxx,max_id;
        bool lan;
    }t[maxm*16+maxn];
    vector<int>path;
    inline int get(int u){return t[t[u].fa].ch[1]==u;}
    inline bool isroot(int u){return t[t[u].fa].ch[0]!=u&&t[t[u].fa].ch[1]!=u;}
    inline void push_up(int u){
        t[u].maxx=t[u].z,t[u].max_id=u;
        if(t[t[u].ch[0]].maxx>t[u].maxx) t[u].maxx=t[t[u].ch[0]].maxx,t[u].max_id=t[t[u].ch[0]].max_id;
        if(t[t[u].ch[1]].maxx>t[u].maxx) t[u].maxx=t[t[u].ch[1]].maxx,t[u].max_id=t[t[u].ch[1]].max_id;
    }
    inline void rotate(int u){
        int fa=t[u].fa,faa=t[fa].fa,d=get(u),dd=get(fa);
        t[u].fa=faa;if(!isroot(fa)) t[faa].ch[dd]=u;
        t[fa].ch[d]=t[u].ch[!d];if(t[u].ch[!d]) t[t[u].ch[!d]].fa=fa;
        t[u].ch[!d]=fa,t[fa].fa=u;
        push_up(fa),push_up(u);
    }
    inline void push_down(int u){
        if(t[u].lan==0) return ;
        t[u].ch[0]^=t[u].ch[1]^=t[u].ch[0]^=t[u].ch[1];
        if(t[u].ch[0]) t[t[u].ch[0]].lan^=1;
        if(t[u].ch[1]) t[t[u].ch[1]].lan^=1;
        t[u].lan=0;
    }
    inline void reverse(int u){t[u].lan^=1;push_down(u);}
    void push_all(int u){
        if(isroot(u)){push_down(u);return ;}
        push_all(t[u].fa);
        push_down(u);
    }
    inline int splay(int u){
        int v=u;
        push_all(u);
        for(;!isroot(u);rotate(u)) if(!isroot(t[u].fa)) rotate(get(t[u].fa)==get(u)?t[u].fa:u);return u;
    }
    inline void access(int u){
        int v=0;
        while(u) splay(u),t[u].ch[1]=v,push_up(u),u=t[v=u].fa;
    }
    inline void make_root(int u){access(u),splay(u),reverse(u);}
    inline int find_root(int u){
        access(u),splay(u);
        while(t[u].ch[0]) push_down(u),u=t[u].ch[0];
        splay(u);
        return u;
    }
    inline void split(int u,int v){make_root(u),access(v),splay(v);}
    inline void new_point(int u,int z){t[u].z=t[u].maxx=z,t[u].max_id=u;}
    inline bool is_merged(int u,int v){make_root(u);return find_root(v)==u;}
    inline void link(int u,int v){make_root(u);if(find_root(v)!=u) t[u].fa=v;}
    inline void cut(int u,int v){split(u,v);t[u].fa=t[v].ch[0]=0;}
public:
    friend class Segment_Tree;
};
class Segment_Tree{
private:
    Link_Cut_Tree t;
    int cnt,top,st1[maxm],st2[maxm],lt[maxm];
    LL ans;
    struct EDGE{int u,v,w;}b[maxm*16+maxn];
    struct Segment_Tree_node{
        vector<EDGE>a;
    }tr[maxtime*8];
    inline void insert(int u,int l,int r,int ll,int rr,EDGE z){
        if(l>rr||r<ll) return ;
        if(ll<=l&&r<=rr){tr[u].a.push_back(z);return ;}
        int mid=l+r>>1;
        insert(u<<1,l,mid,ll,rr,z);
        insert(u<<1|1,mid+1,r,ll,rr,z);
    }
    inline void query(int u,int l,int r){
        int lt=top;
        for(auto x:tr[u].a){
            int d=++cnt,u=x.u,v=x.v,w=x.w;
            b[d-n]={u,v,w};
            t.new_point(d,w);
            if(t.is_merged(u,v)){
                t.split(u,v);
                int id=t.t[v].max_id;
                if(t.t[id].z<=w){cnt--;continue;}
                t.cut(b[id-n].u,id),t.cut(b[id-n].v,id);
                st1[++top]=id;st2[top]=1;
                ans-=t.t[id].z;
            }
            t.link(u,d),t.link(d,v);
            st1[++top]=d;st2[top]=0;
            ans+=w;
        }
        if(l==r) write(ans),write('\n');
        else{
            int mid=l+r>>1;
            query(u<<1,l,mid);
            query(u<<1|1,mid+1,r);
        }
        while(top>lt){
            int d=st1[top];
            int u=b[d-n].u,v=b[d-n].v,w=b[d-n].w;
            if(st2[top]) t.link(u,d),t.link(d,v),ans+=w;
            else t.cut(u,d),t.cut(d,v),ans-=w;
            top--;
        }
    }
public:
    inline void init(){
        read(n,m,q);
        cnt=n;
        for(int i=1;i<=m;i++){
            int u,v,w;
            read(u,v,w);
            b[i]={u,v,w};
        }
        for(int i=1;i<=q;i++){
            int k,d;read(k,d);
            insert(1,1,q,lt[k],i-1,b[k]);
            b[k].w=d;
            lt[k]=i;
        }
        for(int i=1;i<=m;i++) insert(1,1,q,lt[i],q,b[i]);
    }
    inline void query(){query(1,1,q);}
}t;
signed main(){
    t.init();
    t.query();
    return 0;
}
posted @ 2026-06-01 17:24  Link-Cut_Trees  阅读(18)  评论(0)    收藏  举报