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

来源:互联网 发布:易观外卖大数据 编辑:程序博客网 时间:2024/04/27 12:04

题目大意:

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


分析:

SolutionA LCT维护最小生成树

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

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

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

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

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

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

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<set>#define SF scanf#define PF printfusing 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 400010namespace 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);}set<pair<int,int>,greater<pair<int,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);    }}
原创粉丝点击