bzoj3159 決戰 LCT

題目大意:
維護一個樹,支持以下操作:
1、鏈+
2、鏈求和
3、鏈求最大
4、鏈求最小
5、鏈翻轉(此處的翻轉是指把鏈上的值翻轉,而樹的形態不變)

題目分析:(LCT)
如果只有前四個操作就可以用LCT或者樹鏈剖分+線段樹隨便維護一下就行了。
有了第五個操作就不行了。

所以我們用兩個LCT,一個維護這棵樹的形態,另一個維護樹上所有的權值。
兩個LCT的鏈的剖分是一樣的,但是splay的形態不需要相同。

進行所有操作的時候,都要先去形態splay裏查詢這個點在權值splay中的位置,然後在權值splay中做所有與權值相關的操作,包括鏈翻轉。

如果沒有鏈翻轉這個操作,兩個splay中的點應該是一一對應的。
但是有了這個操作之後,權值splay中的某一條鏈翻轉了,而形態splay並沒有翻轉,所以這個對應關係就不正確了。但是兩條鏈的中序遍歷序是一一對應的,鏈翻轉之後仍然對應,所以我們不需要維護每一個點的對應關係,只需要維護鏈的對應關係,在找點的時候只要先在形態splay中查詢這個點的中序遍歷的值,再用這個值去權值線段樹中找對應的點,鏈的對應關係只需要在形態splay中存一個對應鏈內的點即可。

注:在權值splay中,一條鏈的father指針很有可能指向的不是它真正的father,所以在Access的時候要在形態splay中先找到這條鏈的father,再在權值splay中找到對應的點,再把整條鏈的father強制賦成這個點。

在此附上給我講這道題的大大的友鏈:http://blog.csdn.net/neither_nor/article/details/52244025
(↑數組黨與指針黨↓)

#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 52000
using namespace std;
typedef long long LL;
const LL INF=0x3f3f3f3f3f3f3f3fll;
inline LL Max(LL x,LL y) { return x>y?x:y; }
inline LL Min(LL x,LL y) { return x<y?x:y; }
struct splay{
    splay *ch[2],*fa,*col;
    LL val,mx,mi,sum,mark,sz;
    bool rev;
    splay();
    void maintain();
    void push_down();
    void push_up();
    void add_mark(int);
    void Reverse();
    int dir();
    splay* find_k(int);
}*null=new splay(),*root[N];
splay :: splay()
{
    val=sum=0;
    sz=null?1:0;
    mx=null?0:-INF;
    mi=null?0:INF;
    mark=0; rev=false;
    ch[0]=ch[1]=fa=col=null;
}
void splay :: maintain()
{
    sz=ch[0]->sz+ch[1]->sz+1;
    sum=ch[0]->sum+ch[1]->sum+val;
    mx=Max(val,Max(ch[0]->mx,ch[1]->mx));
    mi=Min(val,Min(ch[0]->mi,ch[1]->mi));
    return;
}
void splay :: push_down()
{
    if(mark)
    {
        ch[0]->add_mark(mark);
        ch[1]->add_mark(mark);
        mark=0;
    }
    if(rev)
    {
        ch[0]->Reverse();
        ch[1]->Reverse();
        rev=false;
    }
    if(col!=null)
    {
        if(ch[0]!=null) ch[0]->col=col;
        if(ch[1]!=null) ch[1]->col=col;
    }
    return;
}
void splay :: push_up()
{
    if(~dir()) fa->push_up();
    push_down();
    return;
}
void splay :: add_mark(int v)
{
    if(this==null) return;
    val+=v;
    mx+=v;
    mi+=v;
    sum+=1ll*sz*v;
    mark+=v;
    return;
}
void splay :: Reverse()
{
    if(this==null) return;
    swap(ch[0],ch[1]);
    rev=!rev;
    return;
}
int splay :: dir()
{
    return fa->ch[0]==this?0:fa->ch[1]==this?1:-1;
}
splay* splay :: find_k(int k)
{
    if(this==null) return null;
    push_down();
    static int tmp;
    tmp=ch[0]->sz+1;
    if(k<tmp) return ch[0]->find_k(k);
    if(k>tmp) return ch[1]->find_k(k-tmp);
    return this;
}
void turn(splay *c,int d)
{
    static int k;
    static splay* y;
    y=c->ch[d^1];
    c->ch[d^1]=y->ch[d];
    if(y->ch[d]!=null) y->ch[d]->fa=c;
    y->ch[d]=c;
    y->fa=c->fa;
    if(~(k=c->dir())) c->fa->ch[k]=y;
    c->fa=y;
    c->maintain();
    y->maintain();
    return;
}
void splaying(splay *c)
{
    static int d;
    c->push_up();
    while(~(d=c->dir()))
    {
        if(d==c->fa->dir()) turn(c->fa->fa,d^1);
        turn(c->fa,d^1);
    }
    return;
}
void splaying(splay *c,splay *&_c)
{
    splaying(c);
    splaying(c->col);
    _c=c->col->find_k(c->ch[0]->sz+1);
    splaying(_c);
    return;
}
int n,m,R;
int fir[N],nes[N<<1],v[N<<1],tot=1;
char s[20];
void edge(int x,int y)
{
    v[++tot]=y;
    nes[tot]=fir[x];
    fir[x]=tot;
    return;
}
#define edge(x,y) edge(x,y),edge(y,x)
void dfs(int c,int fa)
{
    root[c]=new splay();
    root[c]->col= new splay();
    root[c]->fa=root[fa];
    root[c]->col->fa=root[fa]->col;
    for(int t=fir[c];t;t=nes[t])
    {
        if(fa==v[t]) continue;
        dfs(v[t],c);
    }
    return;

}
void Access(splay *c)
{
    static splay *_c,*y,*_y;
    y=null; _y=null;
    while(c!=null)
    {
        splaying(c,_c);
        c->col=_c;
        c->ch[1]->col=_c->ch[1];
        c->ch[1]=y; _c->ch[1]=_y; _y->fa=_c;
        c->maintain(); _c->maintain();
        y=c; _y=_c;
        c=c->fa;
    }
    return;
}
void Move_to_root(splay *c)
{
    static splay *_c;
    Access(c);splaying(c,_c);
    c->Reverse();
    _c->Reverse();
    return;
}
splay* extract(splay* x,splay *y)
{
    static splay *_y;
    Move_to_root(x);
    Access(y); splaying(y,_y);
    return _y;
}
int main()
{
    scanf("%d%d%d",&n,&m,&R);
    for(int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        edge(x,y);
    }
    root[0]=null;
    root[0]->col=null;
    dfs(R,0);
    for(int i=1,x,y,z;i<=m;i++)
    {
        scanf("%s",s);
        switch(s[2])
        {
            case 'c':
                scanf("%d%d%d",&x,&y,&z);
                extract(root[x],root[y])->add_mark(z);
                break;
            case 'm':
                scanf("%d%d",&x,&y);
                printf("%lld\n",extract(root[x],root[y])->sum);
                break;
            case 'j':
                scanf("%d%d",&x,&y);
                printf("%lld\n",extract(root[x],root[y])->mx);
                break;
            case 'n':
                scanf("%d%d",&x,&y);
                printf("%lld\n",extract(root[x],root[y])->mi);
                break;
            case 'v':
                scanf("%d%d",&x,&y);
                extract(root[x],root[y])->Reverse();
                break;
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章