【JZOJ5662】【GDOI2018Day1模拟4.17】尺树寸泓(splay+线段树)

Problem

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Hint

这里写图片描述

Solution

  这道题刚看我以为打棵只会旋转、不会进行splay操作的splay,按它说的模拟就行了。
  但是切了T1,滚来思考这道题时,我发现它每次旋转操作都会修改到点x和点y的力量值。设力量值为p,答案为t,则 p x p y 旋转后(设y变成x的父亲),不仅仅是 t x t y 会变动,y的所有祖先的t值都会变动。
  于是,我又傻傻地想,每次旋转时,我们就把x和y旋到根,这样就只用变两个了。但是这样的话,把它们旋到根,有这么多旋转,都会变啊!而且树的形态还会改变啊!!
  所以我苦思冥想,终于想到我其实可以用一棵splay维护树的形态,用另一个什么东西维护答案。我已开始以为可以链剖,但是发现树的形态会改变;所以又想到LCT,但是感觉不大可以。
  这时,我想到以前做过的那些维护子树信息的题都是先将所有点按照dfs序(中序遍历)重标号,这样一来,以某个点为根的子树的那些编号就成了一个区间。
  譬如假设有棵树:
这里写图片描述
  它按dfs序重标号了,所以2的子树是[1..3],1的子树是[1..1],3的子树是[3..3]之类。
  那么,我们这题,它的树要旋转,那子树所含的编号区间会改变啊!
  但是,我们知道,它这种旋转方式类似于splay,它会保持原树的中序遍历(splay的正确性就在于此),所以旋转之后每棵子树所含的编号依然是一个个区间。
  譬如我们按照题目的方法右旋点2,则上图变成:
这里写图片描述
  那么1的子树是[1..3],2的子树是[2..3],3的子树是[3..3]——依然是一段段连续的区间。
  于是,我们可以用一棵只会旋转、不会进行splay操作的splay维护树的形态、每个节点的p值以及以每个节点为根的子树所含的编号区间(因为它每次操作只会改变x和y的信息);然后用一棵线段树维护区间p的乘积,拿来查询答案。而每次旋转只会修改两个p值,所以每次旋转只修改线段树中的两个点。
  时间复杂度: O ( n l o g 2 n )

Code

#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
#define dx dfn[x]
#define dl dfn[l[x]]
#define dr dfn[r[x]]
#define A v<<1
#define B A|1
#define ll long long
#define fo(i,a,b) for(i=a;i<=b;i++)
const int N=2e5+1;
const ll MO=1e9+7;
int i,n,Q,w[N],l[N],r[N],fa[N][2],top,X[N],x,dfn[N],time,son[N][2],fat[N],edge[N][2],opt;
ll v[N],p[N],f[N<<2],val;

void read(int &x)
{
    char c=getchar(); x=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);x=(x<<3)+(x<<1)+c-'0',c=getchar());
}
void scan()
{
    read(n);read(Q);
    fo(i,1,n)
    {
        read(w[i]);read(l[i]);read(r[i]);
        fa[l[i]][0]=i,fa[l[i]][1]=0;
        fa[r[i]][0]=i,fa[r[i]][1]=1;
    }
}

inline bool so(int x){return son[fat[x]][1]==x;}
inline void link(int f,int x,bool d)
{
    if(x)
            son[fat[x]=f][d]=x;
    else    son[f][d]=0;
}
inline void update(int x)
{
    p[x]=v[x];edge[x][0]=edge[x][1]=x;

    int l=son[x][0],r=son[x][1];
    if(l){p[x]=(p[x]+p[l])%MO;edge[x][0]=edge[l][0];}
    if(r){p[x]=(p[x]+p[r])%MO;edge[x][1]=edge[r][1];}
}
//splay

void init()
{
    X[top=1]=1;
    while(top)
    {
        x=X[top];

        if(l[x]&&!dl){X[++top]=l[x];continue;}
        if(!dx)v[dx=++time]=w[x];
        if(r[x]&&!dr){X[++top]=r[x];continue;}

        link(dx,dl,0);
        link(dx,dr,1);
        update(dx);

        top--;
    }
}

struct node
{
    int x,y;
};
ll ksm(ll x,int y)
{
    ll ans=1;
    for(;y;y>>=1)
    {
        if(y&1)ans=ans*x%MO;
        x=x*x%MO;
    }
    return ans;
}

void maketree(int v,int l,int r)
{
    if(l==r){f[v]=p[l];return;}
    int mid=l+r>>1;
    maketree(A,l,mid);  
    maketree(B,mid+1,r);
    f[v]=f[A]*f[B]%MO;
}
void modify(int v,int l,int r,int x)
{
    if(l==r)
    {
        val=val*ksm(f[v],MO-2)%MO;
        f[v]=f[v]*val%MO;
        return;
    }
    int mid=l+r>>1;
    if(x<=mid)
            modify(A,l,mid,x);
    else    modify(B,mid+1,r,x);
    f[v]=f[v]*val%MO;
}
ll query(int v,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)return f[v];
    int mid=l+r>>1;ll ans=1;
    if(x<=mid)ans=query(A,l,mid,x,y);
    if(y>mid)ans=ans*query(B,mid+1,r,x,y)%MO;
    return ans;
}//segment tree

void rotate()
{
    int a=son[x][!opt],y=son[x][opt],b=son[y][!opt],Y=son[y][opt],z=fat[x];
    if(!y)return;
    link(x,b,opt);
    if(z)
            link(z,y,so(x));
    else    fat[y]=0;
    link(y,x,!opt);
    update(x);val=p[x],modify(1,1,n,x);
    update(y);val=p[y],modify(1,1,n,y);
}
void work()
{
    fo(i,1,Q)
    {
        read(opt);read(x);x=dfn[x];
        switch(opt)
        {
            case 0:rotate();break;
            case 1:rotate();break;
            case 2:printf("%lld\n",query(1,1,n,edge[x][0],edge[x][1]));break;
        }
    }
}

int main()
{
    freopen("splay.in","r",stdin);
    freopen("splay.out","w",stdout);
    scan();
    init();
    maketree(1,1,n);
    work();
}

猜你喜欢

转载自blog.csdn.net/qq_36551189/article/details/80003117