传送门
splay或者块链的话是裸题,不过好像块链只能过六个点。。。
线段树的话这道题有一个非常关键的数据特点题目里没有说,就是它给出的区间一定是线段树里的整块,那这样的话就可以直接用线段树来搞,区间打标记然后交换左右儿子就可以了。其实和splay也差不多。
线段树
#include
#include
#include
using namespace std;
#define N 150005
int n,m,x,y;
int a[N],tree[N*4],rev[N*4],ch[N*4][2];
inline void build(int now,int l,int r)
{
int mid=(l+r)>>1;
if (l==r)
{
tree[now]=a[l];
return;
}
ch[now][0]=now<<1,ch[now][1]=now<<1|1;
build(ch[now][0],l,mid);
build(ch[now][1],mid+1,r);
}
inline void pushdown(int now,int l,int r,int mid)
{
if (rev[now])
{
swap(ch[now][0],ch[now][1]);
rev[ch[now][0]]^=1; rev[ch[now][1]]^=1;
rev[now]=0;
}
}
inline void reverse(int now,int l,int r,int lrange,int rrange)
{
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange)
{
rev[now]^=1;
return;
}
pushdown(now,l,r,mid);
if (lrange<=mid) reverse(ch[now][0],l,mid,lrange,rrange);
if (mid+1<=rrange) reverse(ch[now][1],mid+1,r,lrange,rrange);
}
inline void print(int now,int l,int r)
{
int mid=(l+r)>>1;
if (l==r)
{
printf("%d ",tree[now]);
return;
}
pushdown(now,l,r,mid);
print(ch[now][0],l,mid);
print(ch[now][1],mid+1,r);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
while (m--)
{
scanf("%d%d",&x,&y);
reverse(1,1,n,x,y);
}
print(1,1,n);
}
splay
#include
#include
#include
using namespace std;
#define N 150005
#define INF 2100000000
int n,m,x,y,sz,root;
int a[N],key[N],f[N],ch[N][2],delta[N],size[N];
void update(int x)
{
size[x]=1;
if (ch[x][0]) size[x]+=size[ch[x][0]];
if (ch[x][1]) size[x]+=size[ch[x][1]];
}
int build(int l,int r,int fa)
{
if (l>r) return 0;
int mid=(l+r)>>1;
int now=++sz;
key[now]=a[mid]; f[now]=fa;
int lch=build(l,mid-1,now);
int rch=build(mid+1,r,now);
ch[now][0]=lch,ch[now][1]=rch;
update(now);
return now;
}
int get(int x)
{
return ch[f[x]][1]==x;
}
void pushdown(int x)
{
if (x&&delta[x])
{
swap(ch[x][0],ch[x][1]);
delta[ch[x][0]]^=1; delta[ch[x][1]]^=1;
delta[x]=0;
}
}
void rotate(int x)
{
pushdown(f[x]);
pushdown(x);
int old=f[x],oldf=f[old],wh=get(x);
ch[old][wh]=ch[x][wh^1];
f[ch[old][wh]]=old;
ch[x][wh^1]=old;
f[old]=x;
if (oldf) ch[oldf][ch[oldf][1]==old]=x;
f[x]=oldf;
update(old);
update(x);
}
void splay(int x,int tar)
{
for (int fa;(fa=f[x])!=tar;rotate(x))
if (f[fa]!=tar)
rotate((get(x)==get(fa))?fa:x);
if (!tar) root=x;
}
int find(int x)
{
int now=root,fa=0;
while (1)
{
pushdown(now);
if (x<=size[ch[now][0]])
now=ch[now][0];
else
{
x-=size[ch[now][0]]+1;
if (x==0) return now;
now=ch[now][1];
}
}
}
void print(int now)
{
pushdown(now);
if (ch[now][0]) print(ch[now][0]);
if (key[now]!=-INF&&key[now]!=INF)
printf("%d ",key[now]);
if (ch[now][1]) print(ch[now][1]);
}
int main()
{
scanf("%d",&n);
a[1]=-INF; a[n+2]=INF;
for (int i=1;i<=n;++i) scanf("%d",&a[i+1]);
root=build(1,n+2,0);
scanf("%d",&m);
while (m--)
{
scanf("%d%d",&x,&y);
int aa=find(x);
int bb=find(y+2);
splay(aa,0);
splay(bb,aa);
delta[ch[ch[root][1]][0]]^=1;
}
print(root);
}
刚开始有一种线段树十分错误的思路,就是不交换左右儿子,只是打标记和下传标记,有标记的话向右儿子走,否则向左走。可当时我没有加一个统计个数的东西,然后导致了毫无逻辑的混乱。