【最小生成树&LCT】Codeforces603E-Pastoral Oddities

题目大意:

给出一个图,求使得这个图中每个点的度数都为奇数。
求出用前i条边,使图满足条件的情况下,最大边权的最小值。


分析:

SolutionA LCT维护最小生成树

很容易发现一些性质:
如果要使得图中每个点度数为奇数,那么每个联通块中点的个数一定为偶数:

根据题目,为了使最大边权最小,我们可以把每个联通块看成它的最小生成树,那么这棵树一定满足:每个非根节点的儿子结点个数必须为偶数,根节点儿子结点个数必为奇数。否则就会出现度数为奇的情况。

那么我们可以这么考虑:首先根节点的儿子结点个数为奇,那么加上他自己,与根节点直接相连的点的个数就为偶数。每个非根结点都会为树增加偶数个子节点,所以最终整个树上的点一定为偶数个。

为了使图中的所有联通块大小都为偶数,
我们可以发现,加边是绝对无害的:
因为:奇数图+奇数图=偶数图,奇数图+偶数图=奇数图,偶数图+偶数图=偶数图。
所以可见我们要尽量加边。
当然,如果边的两端在同一联通块,我们就要找到环上的最大边权,与当前边权比较,看能否替换掉。

满足每个点度数都为奇数后,
为了使最大边权尽量小,我们也必须拆边,显然,拆边不能使图中生成奇数图,所以每次删去最大的边,再判断一次是否合法即可。

现在,我们需要动态地插边和删边,并询问路径上的最大边权,以及询问联通块大小。能做到的这些操作的数据结构,显然使用LCT。

#include
#include
#include
#include
#include
#define SF scanf
#define PF printf
using namespace std;
void Read(int &x){
    char c;
    bool flag=0;
    while(c=getchar(),c!=EOF&&(c<'0'||c>'9')&&c!='-');
    if(c=='-'){c=getchar();flag=1;}
    x=c-'0';
    while(c=getchar(),c!=EOF&&c>='0'&&c<='9')x=x*10+c-'0';
    if(flag==1)x=-x;
}

#define MAXN 100010
#define MAXM 400010
namespace LCT{
    int fa[MAXM],son[MAXM][2];
    bool pre[MAXM],rev[MAXM];
    int siz[MAXM],maxi[MAXM],val[MAXM],w[MAXM],sum[MAXM],mpos[MAXM];
    void update(int x){
        sum[x]=sum[son[x][0]]+sum[son[x][1]]+w[x];
        siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
        maxi[x]=val[x];
        mpos[x]=x;
        if(maxi[son[x][0]]>maxi[x])
            maxi[x]=maxi[son[x][0]],mpos[x]=mpos[son[x][0]];
        if(maxi[son[x][1]]>maxi[x])
            maxi[x]=maxi[son[x][1]],mpos[x]=mpos[son[x][1]];
    }
    void flip(int x){
        swap(son[x][0],son[x][1]);
        rev[x]^=1;
    }
    void pushdown(int x){
        if(rev[x]){
            flip(son[x][0]);
            flip(son[x][1]);
        }
        rev[x]=0;
    }
    void rotate(int x,int d){
        int y=fa[x];
        fa[x]=fa[y];
        pre[x]=pre[y];
        if(fa[y]&&pre[y])
            son[fa[y]][son[fa[y]][1]==y]=x;
        pre[y]=1;
        son[y][!d]=son[x][d];
        if(son[x][d])
            fa[son[x][d]]=y;
        son[x][d]=y;
        fa[y]=x;
        update(y);
        update(x);
    }
    int sta[MAXM];
    void splay(int x){
        int y,z;
        int pnt=0;
        int x1=x;
        while(pre[x1])
            sta[++pnt]=x1,x1=fa[x1];
        sta[++pnt]=x1;
        for(int i=pnt;i>0;i--)
            pushdown(sta[i]);
        while(pre[x]){
            y=fa[x];
            z=fa[y];
            if(!pre[y]){
                rotate(x,son[y][0]==x);
                break;
            }
            if((son[z][0]==y)==(son[y][0]==x)){
                rotate(y,son[z][0]==y);
                rotate(x,son[y][0]==x);
            }
            else{
                rotate(x,son[y][0]==x);
                rotate(x,son[z][0]==x);
            }
        }
        if(rev[x])
            flip(x);
        update(x);
    }
    int Access(int x){
        int last=0;
        while(x){
            splay(x);
            pre[son[x][1]]=0;
            fa[son[x][1]]=x;
            w[x]+=sum[son[x][1]];
            son[x][1]=last;
            if(last)
                pre[last]=1;
            w[x]-=sum[last];
            update(x);
            last=x;
            x=fa[last];
        }
        return last;
    }
    void MakeRoot(int x){
        flip(Access(x));
        splay(x);
        rev[x]=!rev[x];
    }
    int FindRoot(int x){
        Access(x);
        splay(x);
        int u=x;
        while(son[u][0]){
            pushdown(u);
            u=son[u][0];
        }
        splay(u);
        return u;
    }
    void Link(int x,int y){
        if(FindRoot(x)==FindRoot(y))
            return ;
        flip(Access(x));
        splay(x);
        flip(Access(y));
        splay(y);
        fa[x]=y;
        w[y]+=sum[x];
        sum[y]+=sum[x];
        Access(x);
    }
    void Change(int x,int y){
        val[x]=y;
        Access(x);
    }
    void Cut(int x){
        Access(x);
        splay(x);
        pre[son[x][0]]=0;
        fa[son[x][0]]=0;
        son[x][0]=0;
        update(x);
    }
    void Cut(int x,int y){
        flip(Access(x));
        Cut(y);
    }
    int Query(int x,int y){
        flip(Access(x));
        return maxi[Access(y)];
    }
    int Road(int x,int y){
        flip(Access(x));
        return mpos[Access(y)];
    }
    int Size(int x){
        Access(x);
        splay(x);
        return sum[x];
    }
}
using namespace LCT;
int lu[MAXM],lv[MAXM];
int odd,n,m;
void prepare(){
    for(int i=1;i<=n+m;i++){
        siz[i]=1;
        if(i<=n)
            w[i]=1,sum[i]=1;
    }
}
void addedge(int idx){
    int u=lu[idx];
    int v=lv[idx];
    if(Size(u)%2==1&&Size(v)%2==1) odd-=2;
    Link(n+idx,u);
    Link(n+idx,v);
}
int deledge(int idx){
    int u=lu[idx];
    int v=lv[idx];
    Cut(n+idx,u);
    Cut(n+idx,v);
    //PF("(%d %d %d %d)\n",Size(u),Size(v),u,v);
    if(Size(u)%2==1&&Size(v)%2==1) odd+=2;
    return !(Size(u)%2);
}
setint,int>,greaterint,int> > > q;
pair<int,int> l[MAXM];
int main(){
    Read(n),Read(m);
    prepare();
    int x,y,wt;
    odd=n;
    for(int i=1;i<=m;i++){
        Read(lu[i]),Read(lv[i]),Read(l[i].first);
        l[i].second=i;
        int u=lu[i];
        int v=lv[i];
        val[i+n]=maxi[i+n]=l[i].first;
        if(FindRoot(u)==FindRoot(v)){
            int qs=Road(u,v);
            if(val[qs]>l[i].first){
                deledge(qs-n);
                addedge(i);
                q.erase(q.find(make_pair(l[qs-n].first,qs-n)));
                q.insert(l[i]);
                //PF("{%d %d}\n",q.size(),q.begin()->id);
            }
        }
        else{
            addedge(i);
            q.insert(l[i]);
        }
        //PF("(%d]\n",odd);
        if(odd){
            PF("-1\n");
            continue;
        }
        while(deledge(q.begin()->second))
            q.erase(q.begin());
        addedge(q.begin()->second);
        PF("%d\n",q.begin()->first);
    }
}

你可能感兴趣的:(最小生成树,数据结构,图论,网络流)