BZOJ1500 [NOI2005] 维修数列 (含数据生成器)

题目

http://www.lydsy.com/JudgeOnline/problem.php?id=1500

题解

qwq据popoqqq说这是初学者噩梦级别的题目,有点怕。我觉得关键还是条理要清晰,操作很多可以一个一个写。

上午写了插入和删除操作,这一步的时候程序还很精简,精简到update都忘了写qwq写完一直在找splay的时候为啥没更新到size。

下午三点开始写,先写的翻转,因为简单,就打个tag,然后改一下push,ok。
然后写sum,这个也很好维护,因为之前用splay写过线段树练习,这应该不难,up改一下,好像就没了。
然后是修改,这个tag比较劲了,这里还表现不出来,就push里面改一下v,sum就可以了(对应变量含义在下面结构体部分代码注释里有)。
恩然后我觉得最蛋疼的是最大连续子序列和,因为不能为空有很多要注意的地方,还好以前用线段树写过,要注意的大概都明白(其实这道题不难就是考码力),增加三个,Msum,ls,rs记录信息,然后因为两个儿子都可以为或不为空了,比线段树要恼火一些,然后就是上面修改那个tag要对这三个参数产生影响,以及翻转也是,ls和rs要swap一下。写到这里我的up和push已经臃肿不堪了。其实考点也主要就在这两个地方,模板都会打,做法都知道。

下午好像写了1h左右,然后WA了一发,找datamaker找不到,只有自己写了。拍了一会儿发现,还是标记没整对,打标记的时候没维护当前节点信息。

为了方便大家我把datamaker的代码放上来。

代码

//QWsin
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1000000+10;
const int INF=(1<<30);
int num[maxn];

struct Node{
    //值,子树大小,子树和,子树最大连续和,左/右端连续最大和,翻转,修改
    int v,sz,sum,Msum,ls,rs,rev,tag;
    Node *fa,*ch[2];
    Node(int v):v(v){ls=rs=sum=Msum=v;sz=1;rev=tag=0;fa=ch[0]=ch[1]=NULL;}
    inline void up(){
        sz=1;sum=v;
        int ls0=-INF,rs0=-INF,ls1=-INF,rs1=-INF,sum0=0,sum1=0,Msum0=-INF,Msum1=-INF;
        if(ch[0]) {
            sz+=ch[0]->sz,sum+=ch[0]->sum;
            ls0=ch[0]->ls;rs0=ch[0]->rs;
            sum0=ch[0]->sum;Msum0=ch[0]->Msum;
        }
        if(ch[1]) {
            sz+=ch[1]->sz,sum+=ch[1]->sum;
            ls1=ch[1]->ls;rs1=ch[1]->rs;
            sum1=ch[1]->sum;Msum1=ch[1]->Msum;
        }
        ls=max(ls0,max(sum0+v,sum0+v+max(ls1,0)));
        rs=max(rs1,max(sum1+v,sum1+v+max(rs0,0)));
        Msum=max(max(rs0,0)+v+max(ls1,0),max(Msum0,Msum1));
    }
    inline void push(){
        if(rev){
            if(ch[0])
            {
                ch[0]->rev^=1;
                swap(ch[0]->ls,ch[0]->rs);
                swap(ch[0]->ch[0],ch[0]->ch[1]);
            }
            if(ch[1])
            {
                ch[1]->rev^=1;
                swap(ch[1]->ls,ch[1]->rs);
                swap(ch[1]->ch[0],ch[1]->ch[1]);
            }
            rev=0;
        }
        if(tag){
            if(ch[0]) 
            {
                ch[0]->tag=1,ch[0]->v=v,ch[0]->sum=ch[0]->sz*v;
                if(v>=0) ch[0]->ls=ch[0]->rs=ch[0]->Msum=v*ch[0]->sz;
                else ch[0]->ls=ch[0]->rs=ch[0]->Msum=v;
            }
            if(ch[1]) 
            {
                ch[1]->tag=1,ch[1]->v=v,ch[1]->sum=ch[1]->sz*v;
                if(v>=0) ch[1]->ls=ch[1]->rs=ch[1]->Msum=v*ch[1]->sz;
                else ch[1]->ls=ch[1]->rs=ch[1]->Msum=v;
            }
            tag=0;
        }
    }
}*root,*pool[maxn],*tmp[maxn];

int top=0;
inline void init(){for(int i=0;i<maxn;++i)pool[i]=new Node(0);}
inline void newnode(Node* &p,int v,Node *fa){
    p=pool[top++];*p=Node(v);p->fa=fa;
}
inline void del(Node* &p){pool[--top]=p;p=NULL;}

//用num中的[l,r]构建一棵树 
#define mid ((l+r)>>1)
inline void build(Node* &p,int l,int r,Node *fa)
{
    if(r<l) return ;
    newnode(p,num[mid],fa);
    build(p->ch[0],l,mid-1,p);
    build(p->ch[1],mid+1,r,p);
    p->up();
}
#undef mid

inline int pd(Node *p){return p->fa->ch[1]==p;}
inline int lsz(Node *p){return p->ch[0]?p->ch[0]->sz:0;}

inline void rotate(Node *p)
{
    int c=pd(p)^1;Node *t=p->fa;
    t->ch[c^1]=p->ch[c];
    if(p->ch[c]) p->ch[c]->fa=t;
    if((p->fa=t->fa)) p->fa->ch[pd(t)]=p;
    t->fa=p;p->ch[c]=t;t->up();p->up();
    if(p->fa==NULL) root=p;
}

inline void splay(Node *p,Node *FA)
{
    int pos=0;
    for(Node *t=p;t;t=t->fa) tmp[++pos]=t;
    for(;pos>=1;--pos) tmp[pos]->push();

    for(;p->fa!=FA;rotate(p))
        if(p->fa->fa!=FA) rotate(pd(p)==pd(p->fa)?p->fa:p);
}

inline Node *kth(int k)
{
    Node *p=root;
    while(p){
        p->push();//!!!
        int d=lsz(p);
        if(k==d+1) return p;
        if(k<d+1) p=p->ch[0];
        else k-=d+1,p=p->ch[1];
    }
    return NULL;
}

#define KEY root->ch[1]->ch[0]
//从s的位置开始,插入一段长度为len的区间,值为num中的1~len 
inline void insert(int s,int len)
{
    splay(kth(s),NULL);
    splay(kth(s+1),root);
    build(KEY,1,len,root->ch[1]);
    root->ch[1]->up();root->up();
}

inline void remove(Node* &p)
{
    if(p->ch[0]) remove(p->ch[0]);
    if(p->ch[1]) remove(p->ch[1]);
    del(p);
}

//删除l~r区间的数 
inline void remove(int l,int r)
{
    splay(kth(l-1),NULL);
    splay(kth(r+1),root);
    remove(KEY);
    root->ch[1]->up();
    root->up();
}

inline void reverse(int l,int r)
{
    splay(kth(l-1),NULL);
    splay(kth(r+1),root);
    KEY->rev^=1;
    swap(KEY->ls,KEY->rs);//标记注意
    swap(KEY->ch[0],KEY->ch[1]);
}

inline int sum(int a,int b)
{
    splay(kth(a-1),NULL);
    splay(kth(b+1),root);
    return KEY->sum;
}

inline void make_same(int a,int b,int v)
{
    splay(kth(a-1),NULL);
    splay(kth(b+1),root);
    KEY->v=v;KEY->tag=1;KEY->sum=v*KEY->sz;
    if(v>=0) KEY->ls=KEY->rs=KEY->Msum=KEY->sz * v;//这个地方的标记要注意
    else KEY->ls=KEY->rs=KEY->Msum=v;
}

inline void output_splay(Node *p)
{
    p->push();
    if(p->ch[0]) output_splay(p->ch[0]);
    printf("%d ",p->v);
    if(p->ch[1]) output_splay(p->ch[1]);
}
inline int Msum()
{
    int sz=root->sz;
    if(sz<=2) return 0;
    splay(kth(1),NULL);
    splay(kth(sz),root);
    return KEY->Msum;
}

int main()
{
    int n,m;cin>>n>>m;init();
    for(int i=1;i<=n;++i) scanf("%d",num+i);

    newnode(root,0,NULL);newnode(root->ch[1],0,root);

    insert(1,n);

    char op[20];int a,b,c;
    while(m--)
    {
        scanf("%s",op);
        //注意下标需要+1,长度不能++ 
        if(op[0]=='I')
        {
            scanf("%d%d",&a,&b);++a;
            for(int i=1;i<=b;++i) scanf("%d",num+i);
            insert(a,b);
        }
        else if(op[0]=='D')
        {
            scanf("%d%d",&a,&b);++a;
            remove(a,a+b-1);
        }
        else if(op[0]=='R')
        {
            scanf("%d%d",&a,&b);++a;
            reverse(a,a+b-1);
        }
        else if(op[0]=='G')
        {
            scanf("%d%d",&a,&b);++a;
            if(!b) printf("0\n");
            else printf("%d\n",sum(a,a+b-1));
        }
        else if(op[2]=='K')
        {
            scanf("%d%d%d",&a,&b,&c);++a;
            make_same(a,a+b-1,c);
        }
        else printf("%d\n",Msum());

//      output_splay(root);putchar('\n');
    }
    return 0;
}

datamaker

//QWsin
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
const char msg[][10]={"","INSERT","DELETE","MAKE-SAME","REVERSE","GET-SUM","MAX-SUM"};

inline int r(){return rand()%10*(rand()&1?1:-1);}//随机一个数

int main()
{
    srand(time(0));
    int sz=rand()%10+1;//初始数个数
    int op_cnt=rand()%6+1;//操作个数

    printf("%d %d\n",sz,op_cnt);
    for(int i=1;i<=sz;++i) printf("%d ",r());
    putchar('\n');

    while(1)
    {
        if(!op_cnt) break;
        int op=rand()%6+1;
        if(op!=1&&!sz) continue;
        --op_cnt;
        printf("%s ",msg[op]);

        if(op==1)
        {
            int pos=rand()%(sz+1);
            printf("%d ",pos);
            int tot=rand()%5+1;
            printf("%d ",tot);
            for(int i=1;i<=tot;++i) printf("%d ",r());
            sz+=tot;
        }
        if(op==2||op==4||op==5)
        {
            int pos=rand()%sz+1;
            int len=rand()%(sz-pos+1)+1;
            printf("%d %d",pos,len);
            if(op==2) sz-=len;
        }
        if(op==3)
        {
            int pos=rand()%sz+1;
            int len=rand()%(sz-pos+1)+1;
            printf("%d %d %d",pos,len,r());
        }

        putchar('\n');
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值