bzoj3159 决战 LCT

4 篇文章 0 订阅

题目大意:
维护一个树,支持以下操作:
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值