日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[置顶] hdu 1890 伸展树区间翻转

發(fā)布時間:2025/3/17 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [置顶] hdu 1890 伸展树区间翻转 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

題意: 給你n個數(shù),每次先輸出第i大的數(shù)的位置(如果有多個,選下標小的那個),然后每次將第i個位置到第i大的數(shù)所在位置之間的數(shù)進行翻轉(zhuǎn)。

思路:輸入的數(shù)組可能有多個相同的值,我們可以進行兩次排序把數(shù)組的值變?yōu)?---n(表示第幾大)。

在建伸展樹的時候我們可以順便用pos[i]記錄第i大的數(shù)的節(jié)點標號。

對于第i次操作,我們用col[]數(shù)組記錄翻轉(zhuǎn)標記,每次先把第i大的節(jié)點pos[i]旋轉(zhuǎn)到根,那么它的位置為i+左兒子的個數(shù)。然后左兒子打上翻轉(zhuǎn)標記,最后刪除根。

注意:下放懶惰標記時只要交換左右兒子的節(jié)點標號就可以了,也正因為這個原因,?

下放函數(shù)的位置記得要放在沒有引用任何左右兒子信息之前, 這跟區(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é)點的標號 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可以不寫,因為rotate里面有down, 但區(qū)間翻轉(zhuǎn)要先down在去旋轉(zhuǎn),因為左右兒子會改變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é)點旋到根int x = root;printf("%d ", sz[L]+i);down(x); col[L] ^= 1; down(L); //根down,根的左兒子打翻轉(zhuǎn)標記if(sz[L]) { //有左兒子rto(sz[L], root); //把左兒子的最右邊的點旋到根//刪除根,根的左兒子代替根,新根的右兒子還是原根的右兒子,但父親要修改root = L;ch[root][1] = R;pre[root] = 0;pre[R] = root;}else { //沒有左兒子,直接把右兒子拉到根上來root = ch[root][1];pre[root] = 0;}up(root);}printf("%d\n", n);//最后只剩一個節(jié)點時一定是最后一個, 特判一下。} }spt; int main() {int i;while( ~scanf("%d", &n) && n) {spt.init(n);spt.solve();}return 0; }


?

?

總結(jié)

以上是生活随笔為你收集整理的[置顶] hdu 1890 伸展树区间翻转的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。