sgu187:Twist and whirl -- want to cheat(splay+区间翻转)

来源:互联网 发布:淘宝店铺详情页模板 编辑:程序博客网 时间:2024/06/05 09:46

题意:

裸的区间翻转。

分析:

我们可以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;}

0 0