[置顶] hdu 1890 伸展树区间翻转
生活随笔
收集整理的這篇文章主要介紹了
[置顶] hdu 1890 伸展树区间翻转
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
題意: 給你n個(gè)數(shù),每次先輸出第i大的數(shù)的位置(如果有多個(gè),選下標(biāo)小的那個(gè)),然后每次將第i個(gè)位置到第i大的數(shù)所在位置之間的數(shù)進(jìn)行翻轉(zhuǎn)。
思路:輸入的數(shù)組可能有多個(gè)相同的值,我們可以進(jìn)行兩次排序把數(shù)組的值變?yōu)?---n(表示第幾大)。
在建伸展樹(shù)的時(shí)候我們可以順便用pos[i]記錄第i大的數(shù)的節(jié)點(diǎn)標(biāo)號(hào)。
對(duì)于第i次操作,我們用col[]數(shù)組記錄翻轉(zhuǎn)標(biāo)記,每次先把第i大的節(jié)點(diǎn)pos[i]旋轉(zhuǎn)到根,那么它的位置為i+左兒子的個(gè)數(shù)。然后左兒子打上翻轉(zhuǎn)標(biāo)記,最后刪除根。
注意:下放懶惰標(biāo)記時(shí)只要交換左右兒子的節(jié)點(diǎn)標(biāo)號(hào)就可以了,也正因?yàn)檫@個(gè)原因,?
下放函數(shù)的位置記得要放在沒(méi)有引用任何左右兒子信息之前, 這跟區(qū)間其它操作最大的區(qū)別。
?
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define L ch[x][0] #define R ch[x][1] const int maxn = 100005; int pos[maxn]; //pos[i]表示第i大的數(shù)的節(jié)點(diǎn)的標(biāo)號(hào) int n; struct node {int a, id;bool operator <(const node &t) const {return id < t.id;} }p[maxn]; bool cmp(const node &a, const node &b) {return a.a < b.a || (a.a == b.a && a.id < b.id); } struct splayTree {int sz[maxn], ch[maxn][2], pre[maxn];bool col[maxn];int root, tot;void down(int x) {if(col[x]) {col[L] ^= 1;col[R] ^= 1;swap(L, R);col[x] = 0;}}void up(int x) {sz[x] = sz[L] + sz[R] + 1;}void rotate(int &x, int f) {int y = pre[x], z = pre[y];down(y); down(x);ch[y][!f] = ch[x][f];pre[ch[x][f]] = y;pre[x] = pre[y];if(pre[x]) ch[z][ch[z][1] == y] = x;ch[x][f] = y;pre[y] = x;up(y);}void splay(int &x, int g) {while(pre[x] != g) {int y = pre[x], z = pre[y];down(z); down(y); down(x);//不是區(qū)間翻轉(zhuǎn)的題,這里的down可以不寫(xiě),因?yàn)閞otate里面有down, 但區(qū)間翻轉(zhuǎn)要先down在去旋轉(zhuǎn),因?yàn)樽笥覂鹤訒?huì)改變if(z == g) rotate(x, ch[y][0] == x);else {int f = (ch[z][0] == y);ch[y][!f] == x ? rotate(y, f) : rotate(x, !f);rotate(x, f);}}up(x);if(!g) root = x;}int find(int k) {int x = root;while(sz[L]+1 != k) {down(x);if(sz[L]>= k) x = L;else {k -= sz[L]+1;x = R;}}return x;}void rto(int k, int g) {int x = root;while(1) {down(x);if(sz[L]+1 == k) break;if(sz[L]>= k) x = L;else {k -= sz[L]+1;x = R;}}splay(x, g);}void newNode(int &x, int m, int fa) {x = ++tot;pos[p[m].a] = x;pre[x] = fa;sz[x] = 1;L = R = 0;col[x] = 0;}void build(int &x, int l, int r, int fa) {if(l > r) return;int m = (l + r) >> 1;newNode(x, m, fa);build(L, l, m-1, x);build(R, m+1, r, x);up(x);}void init(int n) {tot = 0;int i;//數(shù)字可能相等,可以把數(shù)字預(yù)處理成1--nfor(i = 1; i <= n; i++) {scanf("%d", &p[i].a);p[i].id = i;}sort(p+1, p+n+1, cmp);for(i = 1; i <= n; i++)p[i].a = i;sort(p+1, p+n+1);build(root, 1, n, 0);}void print(int x) {down(x);printf("x: %d lson: %d rson: %d fa: %d lsz: %d rsz: %d\n", x, L, R, pre[x], sz[L], sz[R]);if(L)print(L);if(R)print(R);}void debug() {printf("root = %d\n", root);print(root);}void solve() {for(int i = 1; i < n; i++) {splay(pos[i], 0); //把值為i的節(jié)點(diǎn)旋到根int x = root;printf("%d ", sz[L]+i);down(x); col[L] ^= 1; down(L); //根down,根的左兒子打翻轉(zhuǎn)標(biāo)記if(sz[L]) { //有左兒子rto(sz[L], root); //把左兒子的最右邊的點(diǎn)旋到根//刪除根,根的左兒子代替根,新根的右兒子還是原根的右兒子,但父親要修改root = L;ch[root][1] = R;pre[root] = 0;pre[R] = root;}else { //沒(méi)有左兒子,直接把右兒子拉到根上來(lái)root = ch[root][1];pre[root] = 0;}up(root);}printf("%d\n", n);//最后只剩一個(gè)節(jié)點(diǎn)時(shí)一定是最后一個(gè), 特判一下。} }spt; int main() {int i;while( ~scanf("%d", &n) && n) {spt.init(n);spt.solve();}return 0; }?
?
總結(jié)
以上是生活随笔為你收集整理的[置顶] hdu 1890 伸展树区间翻转的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 内置函数与lambda匿名函数
- 下一篇: Python3之对象垃圾收集机制浅析