题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=187
题意:给出一个长度为n的数列,每次将某个区间的数字翻转。求最后的序列。
思路:将数列两端增加两个哨兵,也就是现在有n+2个数,其中1到n映射到现在的2到n+1。对于每个区间[A,B],将A-1调整到根节点,将B+1调整到根节点的右子树,那么整个区间就到了根节点右子树的左子树(设该节点为P)。将P标记为翻转。每次查找时翻转标记向下传递,类似于线段树的那种操作。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=130005;
int n,m;
int c[N][2],p[N],s[N],sign[N];
int root;
void update(int f,int x,int flag)
{
p[x]=f;
if(!f) return;
c[f][flag]=x;
s[f]=1+s[c[f][0]]+s[c[f][1]];
}
void down(int x)
{
if(!sign[x]) return;
sign[x]=0;
swap(c[x][0],c[x][1]);
sign[c[x][0]]^=1;
sign[c[x][1]]^=1;
}
void rotate(int x)
{
int P=p[x],G=p[P];
if(c[P][0]==x)
{
update(P,c[x][1],0);
update(x,P,1);
update(G,x,!(c[G][0]==P));
}
else
{
update(P,c[x][0],1);
update(x,P,0);
update(G,x,!(c[G][0]==P));
}
}
void splay(int x,int &goal)
{
int P=p[x],G=p[P],limit=p[goal];
while(P!=limit)
{
if(G!=limit&&(c[G][0]==P)==(c[P][0]==x)) rotate(P);
rotate(x);
P=p[x];
G=p[P];
}
goal=x;
down(goal);
}
int select(int x)
{
int t=root;
while(1)
{
down(t);
if(s[c[t][0]]+1==x) break;
if(s[c[t][0]]+1>x) t=c[t][0];
else x-=s[c[t][0]]+1,t=c[t][1];
}
return t;
}
int build(int L,int R)
{
if(L>R) return 0;
int m=(L+R)>>1;
update(m,build(L,m-1),0);
update(m,build(m+1,R),1);
return m;
}
int main()
{
scanf("%d%d",&n,&m);
root=build(1,n+2);
int A,B,i,r1,r2;
while(m--)
{
scanf("%d%d",&A,&B);
r1=select(A);
r2=select(B+2);
splay(r1,root);
splay(r2,c[r1][1]);
i=c[root][1];
i=c[i][0];
sign[i]^=1;
}
for(i=2;i<=n+1;i++) printf("%d ",select(i)-1);
puts("");
return 0;
}