codeforces 603E(LCT+优先队列)

来源:互联网 发布:甬航数据交互平台 编辑:程序博客网 时间:2024/06/04 20:01

我在比赛(noip模拟)时看到这题,一脸懵逼,上数学课想了很久也想不完整,总有地方实现不了。后来知道了某个dalao用LCT过的,然后和kscla吹B了半个晚上,就把这题吹出来了。

这里写图片描述
这里写图片描述

先讲LCT部分,朴素的LCT可以支持在有Link和Cut操作的森林上修改路径和维护路径信息,比如最大值。
那么就可以维护只有加边情况下的动态最小生成树。
(就是假若出现环,就把环上最大的一条边Cut掉)

Lct还可以维护连通块大小。由于LCT是维护出来的森林是有序的,即一条树链上的结点在Splay上的顺序就是深度由小到大的顺序。而path-parent也是parent,对于一条没有path-parent的路径,它的第一个结点就是这个连通块的根。

既然树是有序的,也就有了相对的子树,我们就可以像静态树一样由下往上维护子树大小。

所以朴素的想法就是按顺序,每条路径将自身子树大小传给path-parent。

可以每个结点开一个other值,表示所有path-parent指向这个点的路径所在子树大小的和。

splay可以维护other的和,记为sum,splay根上的sum+(splay大小)即为这个连通块的大小。

由此也可知,对于只在splay上的操作(不涉及path-parent的更改,如,splay,Cut中的删儿子,翻转),是不会影响other的sum的正确信息的。

现在考虑更改path-parent的操作
Access:Access就是按由下到上顺序把一个点到根的路径扔进splay里,所以就按顺序统计把当前sum算进other里。

Link(x,y):先Evert(x),再splay(x),那么x就能得到x所在连通块正确的信息。
x->path-parent=y。大概想法就是用x所在连通块的信息更新y,y所在splay的path-parent,…,一路更新到根。这样其实不影响复杂度,因为相当于Access(y)。(当然也可以Access(y)后更新一次)

如果学过LCT的套路,以上就是废话了。

对于这题,sunny图的充要条件是每个连通块都有偶数个点。(每个连通块保留一棵生成树,对于每条树边,若该边相连的两个连通块大小都为偶数,则不选该边,否则就选)。
本蒟蒻考试时这点都没看出来……

LCT题的套路:答案在最小生成树上
对于边(x,y),若存在路径(x,y),无论Cut掉路径上哪一条边,边(x,y)与被Cut掉的边都是等价的,同时选或不选。即若不Cut掉的话,当某条边作为答案时,答案需与另一条边取max,故Cut掉长度最大的边最优 。

统计答案用一个维护最大值的优先队列,存树上的边。每次操作后
不断重复:若队头不在树上,或者相连的两个连通块大小都为偶数,则把队头弹出。
这样后,队头就是答案。

我LCT,洋洋洒洒300行,还请教练把时限开大了才能A。

#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>#include <queue>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))#define imax(a, b) (((a)>(b)) ? (a) : (b))typedef long long LL;const int N=800800;void read(int &hy){    hy=0;    char cc=getchar();    while(cc>'9'||cc<'0')    cc=getchar();    while(cc>='0'&&cc<='9')    {        hy=(hy<<3)+(hy<<1)+cc-'0';        cc=getchar();    }}int js,cnt,n,m,u[N],v[N],len[N],fa[N];int find(int x){    if(fa[x]!=x)    fa[x]=find(fa[x]);    return fa[x];}struct yy{    int num,len;      bool operator < (const yy &a)    const{return len<a.len;}}tu;priority_queue<yy>q;struct tree{    tree *c[2],*f,*pp;    int e,me;    bool flip,other,sum,siz,a;    int d(){return f->c[1]==this;}    void sc(tree *x,int d){(c[d]=x)->f=this;}}nil[N],*ro[N],*edge[N];inline tree *newtree(int e,bool a){    nil[++cnt]=nil[0];    nil[cnt].sum=nil[cnt].siz=nil[cnt].a=a;    nil[cnt].me=nil[cnt].e=e;    return nil+cnt;}inline void up(tree *x){    x->siz=x->c[0]->siz^x->c[1]->siz^x->a;    x->me=imax(x->e,imax(x->c[0]->me,x->c[1]->me));    x->sum=x->a^x->other^x->c[0]->sum^x->c[1]->sum;}inline void down(tree *x){    if(!x->flip)    return;    x->flip=0;    swap(x->c[0],x->c[1]);    x->c[0]->flip^=1;    x->c[1]->flip^=1;}void work(tree *x){    if(x->f!=nil)    work(x->f);    down(x);}inline void zig(tree *x){    tree *y=x->f;    int d=x->d();    y->sc(x->c[!d],d);    if(y->f==nil)    x->f=nil;    else    y->f->sc(x,y->d());    x->sc(y,!d);    x->pp=y->pp;    y->pp=nil;    up(y);}inline void splay(tree *x){    work(x);    for(tree *y;x->f!=nil;)    {        y=x->f;        if(y->f!=nil)        (x->d() ^ y->d()) ? zig(x) : zig(y);        zig(x);    }    up(x);}inline void Access(tree *x){    tree *y=nil;    while(x!=nil)    {        splay(x);        if(x->c[1]!=nil)        {            x->c[1]->f=nil;            x->c[1]->pp=x;            x->other^=x->c[1]->sum;        }        x->c[1]=y;        if(y!=nil)        y->f=x;        x->other^=y->sum;        y->pp=nil;        up(x);        y=x;        x=x->pp;    }}inline void Evert(tree *x){    Access(x);    splay(x);    x->flip^=1;}inline void Link(tree *x,tree *y){    Evert(x);    splay(x);    Access(y);    splay(y);    x->pp=y;    y->other^=x->sum;    up(y);}inline void Cut(tree *x,tree *y){    Evert(x);    Access(y);    splay(x);    x->c[1]->f=nil;    x->c[1]=nil;    up(x);}void unite(int num,int X,int Y){    tree *x=ro[X];    tree *y=ro[Y];    X=find(X);    Y=find(Y);    tu.num=num;    tu.len=len[num];    if(X^Y)    {        if(js!=0)        {            Access(x);            splay(x);            Access(y);            splay(y);            if(x->sum&&y->sum)            js-=2;        }        Link(edge[num],x);        Link(edge[num],y);        fa[X]=Y;        q.push(tu);        return;    }    Evert(x);    Access(y);    splay(x);    int le=x->me;    if(le<=len[num])    return;    tree *xx=x;    while(1)    {        if(xx->e==le)        break;        if(xx->c[0]->me==le)        xx=xx->c[0];        else        xx=xx->c[1];    }    Cut(ro[u[xx-nil]],xx);    Cut(ro[v[xx-nil]],xx);    len[xx-nil]=0;    Link(x,edge[num]);    Link(y,edge[num]);    fa[X]=Y;    q.push(tu);}void sora(){    bool hy=true;    while(hy&&!q.empty())    {        hy=false;        tu=q.top();        if(!len[tu.num])        {            q.pop();            hy=true;        }        else        {            tree *x=ro[u[tu.num]];            Cut(x,edge[tu.num]);            Evert(x);            if(!x->sum)            {                q.pop();                hy=true;            }            Link(x,edge[tu.num]);        }    }}int main(){    nil->c[0]=nil->c[1]=nil->f=nil->pp=nil;    cin>>n>>m;    for(int i=1;i<=n;i++)    fa[i]=i;    if(n&1)    {        for(int i=1;i<=m;i++)        printf("-1\n");        return 0;    }    for(int i=1;i<=m;i++)    {        read(u[i]);        read(v[i]);        read(len[i]);        edge[i]=newtree(len[i],0);    }    for(int i=1;i<=n;i++)    ro[i]=newtree(0,1);    int biu;    js=n;    for(biu=1;biu<=m;++biu)    {        unite(biu,u[biu],v[biu]);        if(!js)        break;        printf("-1\n");    }    if(js)    return 0;    sora();    tu=q.top();    printf("%d\n",tu.len);    for(register int i=biu+1;i<=m;++i)    {        unite(i,u[i],v[i]);        sora();        tu=q.top();        printf("%d\n",tu.len);    }    return 0;}

你知道雪为什么是白色的吗,因为她忘了自己曾经的颜色。

阅读全文
1 0