zoukankan      html  css  js  c++  java
  • ## P3206 [HNOI2010]城市建设 线段树分治+LCT

    题意:

    戳这里

    分析:

    • 暴力:

    对于每一个操作,找出它加上或删掉影响的部分中最小的那个值,跟它比较之后决定删除或保留,复杂度 (O(qm))

    • 正解:

    我还在想分成树边和非树边分开考虑怎么计算时,巨佬一眼就切了

    没错,就是线段树分治,因为每一条边被修改中之后等价于删掉这条旧的边,同时加入一条边权为修改的值的新边,这样转换之后相当于 (q+m) 条边,每一条都有一个存在的时间段,按照时间进行分治处理就可以了,同时我们需要一个数据结构支持动态加入和删除边,那么上 (LCT)

    这个题相当于用 (LCT) 动态维护 (MST) ,和 魔法森林 那道题很像,需要转化一下用到一个小 (trick) ,就是说由于 (LCT) 是维护点权的数据结构,没有办法维护边权,所以我们需要新建一个节点来表示边,对于 (x o y) 这条边我们新建一个节点 (z) ,分别连接 (x)(y) 它的点权等价于原来的边权,这个操作和重构树很类似

    之后我们就按照正常方式进行线段树分治,同时 (lct) 每一个点维护区间点权最大的点的编号和值,这样我们就能 (log) 的查询出区间最大的边的位置,总体复杂度就是 (O(nlog ^2)) 但是由于 (LCT) 的复杂度极大,所以在这个题不主动进行卡常优化或吸氧是过不去的

    • 另解:

    由于上面提到的 线段树分治 + (LCT) 的做法不是很优,所以这里提供一个更优秀的做法,复杂度仅有 (O(nlog))(CDQ) 分治

    想法就是,把每一次查询中重复的边我们称之为静态边,就是指那些不会被本次修改的边影响到的边,而会被本次操作影响到的边称之为动态边,而 (CDQ) 分治的过程就是一个不断将动态转化为静态的过程,这样我们按照时间偏序的关系不断递归分治下去,动态边会不断转化为静态边,直到形成一个静态的 (Kruskal)

    栗子:

    对于我们正在处理的 ((l,r)) 这段区间,将它分成 ((l,mid)(mid+1,r)) 两部分

    当我们递归到 ((l,mid)) 时在 ((mid+1,r)) 中的动态边就转化成了静态边

    然后我们用并查集维护静态边集,这样可知动态边集的大小随分治层数变化而变化

    所以复杂度就是 (O(nlog^2))

    我们需要可撤销并查集来动态维护静态边集

    代码:

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define mk(x,y) make_pair(x,y)
    #define pb push_back
    using namespace std;
    
    namespace zzc
    {
        int read()
        {
            int x=0,f=1;char ch=getchar();
            while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
            while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
            return x*f;
        }
        
        const int maxn = 2e6+5;
        int n,m,qt,cnt;
        long long ans[maxn],sum;
    	int pos[maxn],lst[maxn];
        vector<int> t[maxn];
        namespace lct
        {
            int ch[maxn][2],top,q[maxn],fa[maxn],rev[maxn],siz[maxn],val[maxn],mx[maxn];
            void pushup(int x)
            {
                mx[x]=x;
                if(ch[x][0]&&val[mx[ch[x][0]]]>val[mx[x]])mx[x]=mx[ch[x][0]];
                if(ch[x][1]&&val[mx[ch[x][1]]]>val[mx[x]])mx[x]=mx[ch[x][1]];
            }
            void pushdown(int x){int l=ch[x][0],r=ch[x][1];if(rev[x]){swap(ch[x][0],ch[x][1]);rev[l]^=1;rev[r]^=1;rev[x]=0;}}
            bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
            void rotate(int x)
            {
                int y=fa[x],z=fa[y],l,r;
                if(ch[y][0]==x)l=0;else l=1;r=l^1;
                if(!isroot(y)){if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;}
                fa[x]=z;fa[y]=x;fa[ch[x][r]]=y;
                ch[y][l]=ch[x][r];ch[x][r]=y;
                pushup(x);pushup(y);
            }
            void splay(int x)
            {
                q[top=1]=x;
                for(int i=x;!isroot(i);i=fa[i]) q[++top]=fa[i];
                for(int i=top;i;i--) pushdown(q[i]);
                while(!isroot(x))
                {
                    int y=fa[x],z=fa[y];
                    if(!isroot(y))
                    {
                        if((ch[z][0]==y)^(ch[y][0]==x)) rotate(x);
                        else rotate(y);
                    }
                    rotate(x);
                }
                pushup(x);
            }
            void access(int x){for(int t=0;x;t=x,x=fa[x]){splay(x),ch[x][1]=t,pushup(x);}}
            void makeroot(int x){access(x);splay(x);rev[x]=1;}
            int findroot(int x){access(x);splay(x);while(ch[x][0])x=ch[x][0];return x;}
            void split(int x,int y){makeroot(x);access(y);splay(y);}
            void link(int x,int y){makeroot(x);fa[x]=y;}
            void cut(int x,int y){split(x,y);if(ch[y][0]==x&&ch[x][1]==0)ch[y][0]=0,fa[x]=0,pushup(y);}
            int query(int x,int y){split(x,y);return mx[y];}
        }
        using namespace lct;
    
        struct edge
        {
            int frm,to,val;
            edge(){}
            edge(int frm,int to,int val):frm(frm),to(to),val(val){}
        }e[maxn];
        
        void modify(int rt,int l,int r,int ql,int qr,int id)
        {
        	if(ql>qr) return ;
            if(ql<=l&&r<=qr){t[rt].pb(id);return ;}
            int mid=(l+r)>>1;
            if(ql<=mid) modify(rt<<1,l,mid,ql,qr,id);
            if(qr>mid) modify(rt<<1|1,mid+1,r,ql,qr,id);
        }
    
        int tot,st[maxn],flag[maxn];
        void query(int rt,int l,int r)
        {
            int mid=(l+r)>>1,old=tot;
            for(auto i:t[rt])
            {
                int frm=e[i].frm,to=e[i].to,w=e[i].val;
                if(findroot(frm)==findroot(to))
                {
                    int max_id=query(frm,to);
                    if(val[max_id]>w)
                    {
                        cut(e[max_id-n].frm,max_id);
                        cut(e[max_id-n].to,max_id);
                        sum-=e[max_id-n].val;
                        st[++tot]=max_id-n;
                        flag[tot]=1;
                    }
                    else continue;
                }
                link(i+n,frm);link(i+n,to);sum+=w;
                st[++tot]=i;flag[tot]=-1;
            }
            if(l==r) ans[l]=sum;
            else query(rt<<1,l,mid),query(rt<<1|1,mid+1,r);
            for(int i=tot;i>old;i--)
            {
                if(flag[i]<0) 
                {
                    cut(st[i]+n,e[st[i]].frm);
                    cut(st[i]+n,e[st[i]].to);
                    sum-=e[st[i]].val;
                }
                else
                {
                    link(st[i]+n,e[st[i]].frm);
                    link(st[i]+n,e[st[i]].to);
                    sum+=e[st[i]].val;
                }
            }
            tot=old;
        }
    
    
        void work()
        {
            int k,d,x;
            n=read();m=read();qt=read();cnt=m;
            for(int i=1;i<=m;i++) pos[i]=i;
            for(int i=1;i<=m;i++) e[i].frm=read(),e[i].to=read(),e[i].val=read(),lst[i]=1;
            for(int i=1;i<=qt;i++)
            {
                k=read();d=read();x=pos[k];
                modify(1,1,qt,lst[k],i-1,x);lst[k]=i;
                e[++cnt]=e[x];e[cnt].val=d;pos[k]=cnt;
            }
            for(int i=1;i<=cnt;i++) val[i+n]=e[i].val;
            for(int i=1;i<=m;i++) modify(1,1,qt,lst[i],qt,pos[i]);
            query(1,1,qt);
            for(int i=1;i<=qt;i++) printf("%lld
    ",ans[i]);
        }
    }
    
    int main()
    {
        zzc::work();
        return 0;
    }
    
  • 相关阅读:
    2020-07-21日报博客
    java第十九天
    Java第十八章
    Java第十七天
    Java第十六天
    Java第十五天
    Java第十四天
    Java第十三天
    Pytorch-数据增强
    Pytorch-实现ResNet-18并在Cifar-10数据集上进行验证
  • 原文地址:https://www.cnblogs.com/youth518/p/14234321.html
Copyright © 2011-2022 走看看