题目
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;
}