题意:给一个1到n的序列,有两种操作:
①将区间【L,R】的序列截下来,放到截下来的之后的序列的第k个位置后面。
②将区间【L,R】的序列翻转。
题解:(注意要加首尾节点)要取区间的时候要先将L的前驱splay到根节点,R的后继splay到根的儿子节点,之后root的右儿子的左儿子就是区间【L,R】,然后打上翻转标记即可。
AC代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
#define keytree ch[ch[root][1]][0]
#define L(x) ch[x][0]
#define R(x) ch[x][1]
#define N 300010
int ch[N][2],pre[N],cnt[N],size[N],val[N],small[N],rev[N],key[N];
int tot,root;
int a[N],n;
void newnode(int &u,int fa,int w,int KEY)
{
u=++tot;
ch[u][0]=ch[u][1]=rev[u]=0;
pre[u]=fa;size[u]=1;
val[u]=small[u]=w;
key[u]=KEY;
}
void up(int u)
{
size[u]=1+size[L(u)]+size[R(u)];
small[u]=min(val[u],min(small[L(u)],small[R(u)]));
}
void down(int u)
{
if(rev[u])
{
if(L(u))rev[L(u)]^=1;
if(R(u))rev[R(u)]^=1;
swap(L(u),R(u));
rev[u]=0;
}
}
void rotate(int u,int kind)//kind表示u在fa的哪一边
{
int fa=pre[u];
down(fa);down(u);
ch[fa][kind]=ch[u][!kind];
pre[ch[u][!kind]]=fa;
if(pre[fa])ch[pre[fa]][ch[pre[fa]][1]==fa]=u;
pre[u]=pre[fa];
ch[u][!kind]=fa;
pre[fa]=u;
up(fa);up(u);
}
void splay(int u,int goal)
{
int fa,kind;
down(u);
while(pre[u]!=goal)
{
if(pre[pre[u]]==goal)
{
down(pre[u]);down(u);
rotate(u,R(pre[u])==u);
}
else
{
fa=pre[u];
down(pre[u]);down(fa);down(u);
kind=R(pre[fa])==fa;
if(ch[fa][kind]!=u)//不在同一侧
{
rotate(u,!kind);
rotate(u,kind);
}
else
{
rotate(fa,kind);
rotate(u,kind);
}
}
}
up(u);
if(goal==0)root=u;
}
int getkth(int u,int k)//第k个键值的点的编号
{
down(u);
int s=size[L(u)]+1;
if(s==k) return u;
if(s>k) return getkth(L(u),k);
else return getkth(R(u),k-s);
}
int find(int u,int x)//查找键值为x的点的编号
{ // 有反转标记时不可用
down(u);
if(key[u]==x)return u;
if(key[u]>x)
{
if(!L(u))return -1;
return find(L(u),x);
}
if(key[u]<x)
{
if(!R(u))return -1;
return find(R(u),x);
}
}
int getpre(int u)
{
down(u);u=L(u);down(u);
while(R(u))
{
u=R(u);
down(u);
}
return u;
}
int getnext(int u)
{
down(u);u=R(u);down(u);
while(L(u))
{
u=L(u);
down(u);
}
return u;
}
void del(int x)//删除编号为x的节点
{
if(cnt[x]>1)
{
cnt[x]--;
return ;
}
splay(x,0);
if(L(root))
{
int p=getpre(x);
splay(p,root);
R(p)=R(root);
pre[R(root)]=p;
root=p;
pre[p]=0;
up(root);
}
else
{
root=R(root);
pre[root]=0;
}
}
void build(int &u,int l,int r,int fa)//按pos为键值
{ //val为数的大小 a存数的大小
if(l>r)return ;
int mid=(l+r)>>1;
newnode(u,fa,a[mid],mid);
build(L(u),l,mid-1,u);
build(R(u),mid+1,r,u);
up(u);
}
void init()
{
root=tot=0;
L(root)=R(root)=pre[root]=size[root]=rev[root]=0;
val[root]=small[root]=N;
newnode(root,0,N,0);
newnode(R(root),root,N,N);
build(keytree,1,n,R(root));
up(R(root));
up(root);
}
int getmin(int u,int x)//得到最小值的相对位置
{
down(u);
if(val[u]==x) return 1+size[L(u)];
if(small[L(u)]==x) return getmin(L(u),x);
if(small[R(u)]==x) return size[L(u)]+1+getmin(R(u),x);
}
//--------------------------------------------------基本操作
int g=0;
void dfs(int root)
{
if(root==0)return ;
down(root);
dfs(ch[root][0]);
if(key[root]!=0&&key[root]!=N)
{ g++;
if(g<n)printf("%d ",key[root]);
else printf("%d\n",key[root]);
}
dfs(ch[root][1]);
}
int main()
{
int m;
while(~scanf("%d%d",&n,&m))
{
if(n<0&&m<0)break;
for(int i=1;i<=n;i++)
a[i]=i;
g=0;
init();
for(int i=0;i<m;i++)
{
char op[10];
scanf("%s",op);
if(op[0]=='C')
{
int l,r,p;
scanf("%d%d%d",&l,&r,&p);
int pos=getkth(root,l);
splay(pos,0);
pos=getkth(root,r+2);
splay(pos,root);
int now=keytree;
ch[ch[root][1]][0]=0;
pos=getkth(root,p+1);
splay(pos,0);
pos=getkth(root,p+2);
splay(pos,root);
ch[ch[root][1]][0]=now;
pre[now]=ch[root][1];
}
else
{
int l,r;
scanf("%d%d",&l,&r);
int pos=getkth(root,l);
splay(pos,0);
pos=getkth(root,r+2);
splay(pos,root);
rev[keytree]^=1;
}
}
dfs(root);
}
}

本文介绍了一种解决区间操作问题的方法,通过使用线段树实现区间翻转和区间移动的功能。具体包括如何构建线段树、进行区间翻转、区间移动等关键步骤,并提供了完整的AC代码。
491

被折叠的 条评论
为什么被折叠?



