懒得粘题,直接写题解吧。
这道题目的话是道非常简单的平衡树的题目。
我采用区间操作的佼佼者:FHQ Treap。
我们来考虑:
ADD操作可以给这个区间打懒标记,FHQ可以直接把这个区间分离出来。
REVERSE也是打标记,如果有标记就交换左右儿子,并且下传标记
REVOLVE则更简单,首先先让$T$%$(y-x+1)$,然后我们会发现,轮换的本质其实就是把$[x,y]$右边的$T$个数放到左边。
剩下的三个操作也都是基本操作。
这道题可以说算是挺简单的区间操作的平衡树。
记得开long long。
不知从什么时候开始我开始压行了QAQ
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define N 210000
using namespace std;
typedef long long LL;
LL son[N][2],size[N],key[N],val[N],n,la1[N],la2[N],mmin[N],root,len,m;
inline LL mymin(LL x,LL y){return x<y?x:y;}
inline void update(LL x){size[x]=size[son[x][0]]+size[son[x][1]]+1;mmin[x]=mymin(mymin(mmin[son[x][0]],mmin[son[x][1]]),key[x]);}//记录左右儿子的信息
inline void fan(LL x){son[x][0]^=son[x][1]^=son[x][0]^=son[x][1];la1[son[x][0]]^=1;la1[son[x][1]]^=1;la1[x]=0;}//将x的左右儿子翻转
inline void jia(LL x){mmin[x]+=la2[x];key[x]+=la2[x];la2[son[x][0]]+=la2[x];la2[son[x][1]]+=la2[x];la2[x]=0;}
void pushdown(LL x)//点标记
{
if(la1[x])fan(x);//翻转查自己的标记
if(la2[son[x][0]])jia(son[x][0]);//加值直接更新儿子,因为在spilt和merge里面左右儿子不更新的话,在update会将mmin更新会原样,导致错误。
if(la2[son[x][1]])jia(son[x][1]);
}
//简单的合并
LL merge(LL x,LL y)
{
if(!x || !y)return x+y;
pushdown(x);pushdown(y);
if(val[x]<=val[y])son[x][1]=merge(son[x][1],y);
else son[y][0]=merge(x,son[y][0]),x^=y^=x^=y;
update(x);
return x;
}
void spilt(LL now,LL &x,LL &y,LL k)
{
if(!now)x=0,y=0;
else
{
pushdown(now);
if(size[son[now][0]]+1<=k)x=now,spilt(son[x][1],son[x][1],y,k-size[son[now][0]]-1),update(x);
else y=now,spilt(son[y][0],x,son[y][0],k),update(y);
}
}
void add(LL vl,LL id)//区间加
{
len++;size[len]=1;key[len]=vl;val[len]=rand();mmin[len]=vl;
LL x,y;spilt(root,x,y,id);
root=merge(merge(x,len),y);
}
void revolve(LL l,LL r,LL k)//轮换
{
k=k%(r-l+1);
if(k)
{
LL x1,x2,x3,x4;
spilt(root,x1,x2,l-1);spilt(x2,x2,x4,(r-l+1));spilt(x2,x2,x3,(r-l+1)-k);
root=merge(merge(x1,x3),merge(x2,x4));
}
}
LL getsum(LL l,LL r)//MIN
{
LL x,y,z;spilt(root,x,y,l-1);spilt(y,y,z,r-l+1);
LL ans=mmin[y];
root=merge(x,merge(y,z));
return ans;
}
void fanzhuan(LL l,LL r)//翻转
{
LL x,y,z;spilt(root,x,y,l-1);spilt(y,y,z,r-l+1);
la1[y]^=1;
root=merge(x,merge(y,z));
}
void jia(LL l,LL r,LL c)//在某个位置加个点。。。
{
LL x,y,z;spilt(root,x,y,l-1);spilt(y,y,z,r-l+1);
la2[y]+=c;jia(y);
root=merge(x,merge(y,z));
}
void del(LL id)//删除
{
LL x,y,z;spilt(root,x,y,id-1);spilt(y,y,z,1);
root=merge(x,z);
}
int main()
{
srand(999);
mmin[0]=99999999;
scanf("%lld",&n);
for(LL i=1;i<=n;i++)
{
LL x;scanf("%lld",&x);
add(x,i-1);
}
scanf("%lld",&m);
for(LL i=1;i<=m;i++)
{
char st[20];LL x,y,z;
scanf("%s",st+1);
if(st[1]=='A')
{
scanf("%lld%lld%lld",&x,&y,&z);
jia(x,y,z);
}
else if(st[1]=='R' && st[4]=='E')
{
scanf("%lld%lld",&x,&y);
fanzhuan(x,y);
}
else if(st[1]=='R' && st[4]=='O')
{
scanf("%lld%lld%lld",&x,&y,&z);
revolve(x,y,z);
}
else if(st[1]=='I')
{
scanf("%lld%lld",&x,&y);
add(y,x);
}
else if(st[1]=='D')
{
scanf("%lld",&x);
del(x);
}
else if(st[1]=='M')
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",getsum(x,y));
}
}
return 0;
}
%%%
插入点的函数注释的是区间加,区间加的函数注释的是插入点(似乎?)
千古神犇zjj,扑通扑通跪下来
zjj太可怕啦!!!一年前就A啦
好像可以不用开
lonh long
懒得看,开了保险
又看了一下啊,如果所有都开int会WA,我把int全部改了long long就A了,当然有些应该是不用开long long的