Luo Gu P3206 [HNOI2010] Urban Construction

Immortal title

Subject to the effect:

There is a \ (n-\) points \ (m \) minimum weight spanning tree edges without this graph to FIG Unicom, each modification of the right side of an edge, and after each modification to ask

Saying is not \ (cdq \) subject what data structure can be used Mang past ah ......

This question is indeed \ (cdq \) Good question

\ (cdq \) partition must deal with multi-dimensional partial order problem? Not, its core idea is to calculate a contribution to another sub-issues with sub-problems

This question we will follow the timeline to divide and conquer, then clearly a sub-sub-issue is another problem exists contribution

We will be on the side of the entire map classification:

Side of the partition in the current interval is referred to as dynamic involves modifying the edge

Other side called static edges

In the process, we divide and conquer, dynamic side will obviously be reduced as the range is reduced, then we how to deal with the static side of it?

Careful consideration, wenot at allCan be found, the static side can be divided into three categories:

\ (1 \) useless side: certainly not a minimum spanning tree edge

For these sides, we can directly throw away

\ (2 \) necessary edges: Required as spanning tree edge

For this side, we can directly answer included the right side, then the edge connecting two points with the investigation and the collection and together

\ (3 \) other side

No way, directly to the sub-interval it

According to this method, each partition to a smaller range, the dynamic range of the other side will become static side, when we divide and conquer to \ (l == r \) , all sides will become a static side , then the other side will not present the necessary contributions to the edge of the answer is the answer

Now we have two questions:

1 : How to find the first two categories edge?

Seeking unwanted sideband, we can set up the right side all the dynamic side of \ (INF \) , then run \ (Kruskal \) , still has not been used in the last edges are unwanted sideband

Seek the necessary edge, we can set up the right side all the dynamic side of \ (- INF \) , also ran \ (Kruskal \) , last used static side edges are necessary

Correctness seems to be quite obvious (fog

2 : time complexity?

Suppose the current interval \ (K \) pieces of dynamic edge, then the edge, and after the necessary removal of unwanted sideband, leaving up \ (k + 1 \) point and \ (2K \) edges

Because of \ (K \) each reduced twice, the number of edges and points are both decremented by almost double the complexity of about \ (O (nlog ^ 2n) \)

Note that every time we need to seek first operation side and then seek unwanted sideband complexity fishes! ! !

We pay attention to this question, it's the other sub-sub-problems contribute to the current problem is not in accordance with the general \ (cdq \) method, but before entering the current interval has contributed calculate the

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define mid ((l+r)>>1)
#define lowbit(x) ((x)&(-x))
    inline int read()
    {
        int x=0;char ch,f=1;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    const int N=1e5+10;
    int n,m,ask;
    struct edge
    {
        int x,y,val,opt;
        inline bool operator < (const edge &t) const
        {
            return val<t.val;
        }
    }a[N];
    struct moved
    {
        int x,y;
    };
    struct node
    {
        int num,val,ret;
    }q[N];
    struct dsu
    {
        int f[N],str[N];
        struct blue {int x,y;};
        stack<blue> st;
        inline void init()
        {
            for(int i=1;i<=n;++i) f[i]=i,str[i]=1;
        }
        inline int find(int k)
        {
            return f[k]==k?k:find(f[k]);
        }
        inline void merge(int x,int y)
        {
            int tx=find(x),ty=find(y);
            if(tx==ty) return;
            if(str[tx]<str[ty]) swap(tx,ty);
            str[tx]+=str[ty];
            f[ty]=tx;
            blue tmp;
            tmp.x=tx,tmp.y=ty;
            st.push(tmp);
        }
        inline void del()
        {
            blue now=st.top();
            st.pop();
            f[now.y]=now.y;
            str[now.x]-=str[now.y];
        }
        inline void clear(int bot)//可撤销并查集
        {
            while(st.size()>bot) del();
        }
    }s1,s;//s全局
    int ret[30];
    //reduction无用边  
    //contraction必须边
    vector<edge> eg[30],tr;//存每一层静态边和临时静态边
    vector<moved> rose;//存当前动态边
    int bot[30];
    bool book[N];
    inline void push_down(int dep)
    {
        tr.clear();
        for(int i=0;i<eg[dep].size();++i) tr.push_back(eg[dep][i]);
        sort(tr.begin(),tr.end());
        s1.clear(0);
        ret[dep+1]=ret[dep];
        for(int i=0;i<rose.size();++i) s1.merge(rose[i].x,rose[i].y);
        rose.clear();
        for(int tx,ty,i=0;i<tr.size();++i)//求必要边
        {
            tx=s1.find(tr[i].x),ty=s1.find(tr[i].y);
            if(tx==ty) continue;
            tr[i].opt=1;
            s1.merge(tr[i].x,tr[i].y);
            s.merge(tr[i].x,tr[i].y);
            ret[dep+1]+=tr[i].val;
        }
        s1.clear(0);//撤销
        for(int i=0;i<tr.size();++i)//求无用边
        {
            if(tr[i].opt) continue;
            if(s.find(tr[i].x)==s.find(tr[i].y)||s1.find(tr[i].x)==s1.find(tr[i].y))
            {
                tr[i].opt=-1;
                continue;
            }
            s1.merge(tr[i].x,tr[i].y);
        }
        s1.clear(0);//撤销
        eg[dep+1].clear();
        for(int tx,ty,i=0;i<tr.size();++i)
        {
            if(tr[i].opt) continue;
            tx=s.find(tr[i].x),ty=s.find(tr[i].y);
            if(tx==ty) continue;
            edge tmp;
            tmp.x=tx,tmp.y=ty,tmp.val=tr[i].val,tmp.opt=0;
            eg[dep+1].push_back(tmp);
        }
        return;
    }
    inline void cdq(int l,int r,int dep)
    {
        bot[dep]=s.st.size();
        if(l==r)
        {
            edge tmp;
            tmp.x=s.find(a[q[r].num].x),tmp.y=s.find(a[q[r].num].y);//底层
            tmp.val=q[r].val,tmp.opt=0;
            a[q[r].num].val=q[r].val;//注意这里,直接更改
            eg[dep].push_back(tmp);
            push_down(dep);
            q[r].ret=ret[dep+1];
            s.clear(bot[dep-1]);
            return;
        }
        for(int i=l;i<=mid;++i) book[q[i].num]=1;//递归左边时,右边的动态变静态
        for(int i=mid+1;i<=r;++i)
        {
            if(book[q[i].num]) continue;
            edge tmp;
            tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
            tmp.val=a[q[i].num].val,tmp.opt=0;
            eg[dep].push_back(tmp);
        }
        for(int i=l;i<=mid;++i)//左边的动态存进去
        {
            moved tmp;
            tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
            rose.push_back(tmp);
        }
        
        push_down(dep);//下放
        
        for(int i=mid+1;i<=r;++i)
        {
            if(book[q[i].num]) continue;
            eg[dep].pop_back();
        }
        for(int i=l;i<=mid;++i) book[q[i].num]=0;//回溯
        
        cdq(l,mid,dep+1);
        
        for(int i=0;i<eg[dep].size();++i) eg[dep][i].opt=0;//去掉标记
        
        for(int i=mid+1;i<=r;++i) book[q[i].num]=1;
        
        for(int i=l;i<=mid;++i)//左边的动态变静态
        {
            if(book[q[i].num]) continue;
            edge tmp;
            tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
            tmp.val=a[q[i].num].val,tmp.opt=0;
            eg[dep].push_back(tmp);
        }
        for(int i=mid+1;i<=r;++i)//右边的动态放进去
        {
            book[q[i].num]=0;
            moved tmp;
            tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
            rose.push_back(tmp);
        }
        push_down(dep);
        cdq(mid+1,r,dep+1);
        s.clear(bot[dep-1]);//撤销并查集
    }
    inline void main()
    {
        n=read(),m=read(),ask=read();
        s1.init(),s.init();
        for(int i=1;i<=m;++i) a[i].x=read(),a[i].y=read(),a[i].val=read();
        for(int i=1;i<=ask;++i)
        {
            q[i].num=read(),q[i].val=read();
            book[q[i].num]=1;
        }
        for(int i=1;i<=m;++i)
        {
            if(book[i]) continue;
            eg[1].push_back(a[i]);
        }
        for(int i=1;i<=ask;++i) book[q[i].num]=0;
        cdq(1,ask,1);
        for(int i=1;i<=ask;++i) printf("%lld\n",q[i].ret);
    }
}
signed main()
{
    red::main();
    return 0;
}

Guess you like

Origin www.cnblogs.com/knife-rose/p/12045917.html