题意:
裸的区间翻转。
分析:
我们可以ws的用一下stl的reverse,如果手写的话splay较好。
类似线段树lazy思想,我们找到l-1项和r+1项,将其分别splay到root和右孩子,之间夹着的区间我们给其一个翻转。
问题①:找到当前序列中的第k项?
我们需要对每一个结点记录以该结点为根的子树的结点数size,从root出发向做左或向右找。
我们需要在rotate的时候更新size。
问题②:如何翻转?
找到l-1和r+1后,将其夹着的区间的根结点的rev值^1即可。
问题③:何时rev起作用?
Ⅰrotate之前将其传给子结点;
Ⅱ找第k项结点时,每往下走一步时,都要将其传给子结点。
其他的问题应该可以随意脑补了。
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 130009;
int n, m;
int f[MAXN], c[MAXN][2];
int root;
int size[MAXN];
bool rev[MAXN];
int maketree(int l, int r, int fa)
{
int mid = (l+r)>>1;
f[mid] = fa;
if(l <= mid-1) c[mid][0] = maketree(l, mid-1, mid);
if(mid+1 <= r) c[mid][1] = maketree(mid+1, r, mid);
size[mid] = size[c[mid][0]]+size[c[mid][1]]+1;
return mid;
}
void down(int p)
{
if(!rev[p]) return ;
rev[p] = 0;
swap(c[p][0], c[p][1]);
rev[c[p][0]] ^= 1;
rev[c[p][1]] ^= 1;
}
void rotate(int s, int &root)
{
down(s);
int x = f[s], y = f[x];
int p = (s==c[x][1]), q = p^1;
int fp = (x==c[y][1]);
if(x == root) root = s;
else c[y][fp] = s;
size[s] = size[x];size[x] = size[c[s][q]]+size[c[x][q]]+1;
f[s] = y;f[x] = s;f[c[s][q]] = x;
c[x][p] = c[s][q];c[s][q] = x;
}
void splay(int p, int &root)
{
while(p != root)
{
int x = f[p], y = f[x];
if(x != root)
{
if((c[x][0] == p)==(c[y][0] == x)) rotate(x, root);
else rotate(p, root);
}
rotate(p, root);
}
}
int find(int p)
{
int now = root;
while(1)
{
down(now);
if(p == size[c[now][0]]+1) break;
if(p > size[c[now][0]]) p -= size[c[now][0]]+1, now = c[now][1];
else now = c[now][0];
}
return now;
}
int main()
{
scanf("%d%d", &n, &m);
root = (1+n+2)>>1;
maketree(1, n+2, 0);
for(int i = 1, l, r; i <= m; ++i)
{
scanf("%d%d", &l, &r);
int x = find(l), y = find(r+2);
splay(x, root);
splay(y, c[root][1]);
rev[c[y][0]] ^= 1;
}
for(int i = 2; i <= n+1; ++i)
printf("%d ", find(i)-1);
return 0;
}