poj 3580 SuperMemo

题意:要求维护六种操作:区间加,区间翻转,区间切割(旋转),单点插入删除,区间求最小值


【分析】
整整调了三节课+一个晚上(一点才睡觉啊qwq)…啊尼玛
总的来说这题就是暴力splay

其中有许多子问题,比如区间翻转&区间旋转,参见hdu 3487 play with chain,如果没做过可以先做一下,这样的话更容易调这个程序。

总的来说要修改一个区间[x,y]就是将节点x-1 splay到根,将y+1 splay到根的右子树,然后根的右子树的左子树就是区间[x,y],可以在这上面打一个标记,然后下传。

插入操作与上面的操作相似:把x翻转到根,把x+1翻转到根的右子树,那么根的右子树的左子树为空,新加一个节点即可。

删除与插入类似。

区间加法比较玄妙了…pushdown的时候要将当前节点的左右子树的key(当前权值),add(标记值),mn(维护的最小值)都要加上一个add[now]。

差不多就是这样。谢谢观赏。代码略长176行,仅供参考。

推荐:Monster_Yi写的代码只用了不到2KB,还有谁。


【代码】

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
#define inf 0x3f3f3f3f
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=210000;
int n,m,sz,opt,root;
int a[mxn],f[mxn],mn[mxn],ch[mxn][2],key[mxn],size[mxn],add[mxn],reverse[mxn];
inline void clear(int x)
{
    f[x]=mn[x]=ch[x][0]=ch[x][1]=key[x]=size[x]=add[x]=reverse[x]=0;
}
inline int get(int x)
{
    if(ch[f[x]][1]==x) return 1;return 0;
}
inline void pushdown(int now)   
{
    if(!now) return;
    int lson=ch[now][0],rson=ch[now][1];
    if(reverse[now])
    {
        if(lson) reverse[lson]^=1;
        if(rson) reverse[rson]^=1;
        swap(ch[now][0],ch[now][1]);
        reverse[now]=0;
    }
    if(add[now])
    {
        if(lson) add[lson]+=add[now],key[lson]+=add[now],mn[lson]+=add[now];
        if(rson) add[rson]+=add[now],key[rson]+=add[now],mn[rson]+=add[now];
        add[now]=0;
    }
}
inline void update(int x)
{
    if(!x) return;
    size[x]=1;
    if(ch[x][0]) size[x]+=size[ch[x][0]];
    if(ch[x][1]) size[x]+=size[ch[x][1]];
    mn[x]=key[x];
    if(ch[x][0]) mn[x]=min(mn[x],mn[ch[x][0]]);
    if(ch[x][1]) mn[x]=min(mn[x],mn[ch[x][1]]);
}
inline void newnode(int now,int d,int fa)
{
    key[now]=d,f[now]=fa,mn[now]=d,add[now]=reverse[now]=ch[now][0]=ch[now][1]=0,size[now]=1;
}
inline void rotate(int x)
{
    pushdown(x);
    int fa=f[x],fafa=f[fa],which=get(x);
    ch[fa][which]=ch[x][which^1],f[ch[fa][which]]=fa;
    ch[x][which^1]=fa,f[fa]=x;
    f[x]=fafa;
    if(fafa) ch[fafa][ch[fafa][1]==fa]=x;
    update(fa),update(x);
}
inline void splay(int x,int lastfa)
{
    for(int fa;(fa=f[x])!=lastfa;rotate(x))
      if(f[fa]!=lastfa) rotate(get(x)==get(fa)?fa:x); 
    if(!lastfa) root=x;
}
inline int build(int fa,int l,int r)
{
    if(l>r) return 0;
    int now=++sz,mid=l+r>>1;
    key[now]=a[mid],f[now]=fa,mn[now]=key[now],size[now]=1;
    ch[now][0]=build(now,l,mid-1);
    ch[now][1]=build(now,mid+1,r);
    update(now);
    return now;
}
inline int number(int x)
{
    int now=root;
    while(1)
    {
        pushdown(now);
        if(ch[now][0] && x<=size[ch[now][0]]) now=ch[now][0];
        else
        {
            if(x==size[ch[now][0]]+1) return now;
            x=x-size[ch[now][0]]-1;
            now=ch[now][1];pushdown(now);
        }
    }
}

int main()
{
    int i,j,x,y,d,t,z;
    char c[105];
    scanf("%d",&n);
    fo(i,2,n+1) scanf("%d",&a[i]);
    a[1]=inf,a[n+2]=inf;
    root=build(0,1,n+2);
    scanf("%d",&m);
    while(m--)
    {
        scanf("%s",c);
        if(c[0]=='A')   //区间+ 
        {
            scanf("%d%d%d",&x,&y,&d);
            x++,y++;
            x=number(x-1),y=number(y+1);
            splay(x,0),splay(y,x);
            add[ch[ch[root][1]][0]]+=d;
            key[ch[ch[root][1]][0]]+=d;
            mn[ch[ch[root][1]][0]]+=d;
        }
        if(c[0]=='R' && c[3]=='E')   //区间翻转 
        {
            scanf("%d%d",&x,&y);
            if(x==y) continue;
            x++,y++;
            x=number(x-1),y=number(y+1);
            splay(x,0);
            splay(y,x);
            reverse[ch[ch[root][1]][0]]^=1;
        }
        if(c[0]=='R' && c[3]=='O')  //区间旋转(拿出来,插进去) 
        {
            scanf("%d%d%d",&z,&y,&t);
            if(z==y) continue;
            t=((t%(y-z+1))+y-z+1)%(y-z+1);
            if(!t) continue;
            y++,x=y-t+1;
            x=number(x-1),y=number(y+1);
            splay(x,0),splay(y,x);
            int tmp=ch[ch[root][1]][0];
            ch[ch[root][1]][0]=0;
            int z1=number(z);
            int z2=number(z+1);
            splay(z1,0),splay(z2,z1);
            ch[ch[root][1]][0]=tmp,f[tmp]=ch[root][1];
        }
        if(c[0]=='I')
        {
            scanf("%d%d",&x,&d);
            x++;
            int x1=number(x);
            int x2=number(x+1);
            splay(x1,0),splay(x2,x1);
            newnode(++sz,d,ch[root][1]);
            ch[ch[root][1]][0]=sz;
        }
        if(c[0]=='D')
        {
            scanf("%d%d",&x,&d);
            x++;
            int x1=number(x-1);
            int x2=number(x+1);
            splay(x1,0);
            splay(x2,x1);
//            clear(ch[ch[root][1]][0]); 
            ch[ch[root][1]][0]=0;
        }
        if(c[0]=='M')
        {
            scanf("%d%d",&x,&y);
            x++,y++;
            x=number(x-1);
            y=number(y+1);
            splay(x,0),splay(y,x);
            pushdown(ch[ch[root][1]][0]);
            update(ch[ch[root][1]][0]);
            printf("%d\n",mn[ch[ch[root][1]][0]]);
        }
    }
    return 0;
}
/*
4
1 2 3 4
10
ADD
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值