BZOJ 3223 Tyvj 1729 文艺平衡树
题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=3223
题目大意:
维护1-n的数组,每次操作翻转一个区间。
思路:
如何找到要操作的区间[l,r]:将当前排名(size)为l-1 +1 的节点转到根,将当前排名为r+2的节点转到根的右子树的根节点,则根的右子树的根节点的左子树为所求区间,直接打标记就可以了。
1 #include<bits/stdc++.h> 2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf 3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时 4 #define Min(a, b) ((a) < (b) ? (a) : (b)) 5 #define Mem(a) memset(a, 0, sizeof(a)) 6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1)) 7 #define MID(l, r) ((l) + ((r) - (l)) / 2) 8 #define lson ((o)<<1) 9 #define rson ((o)<<1|1) 10 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂 11 using namespace std; 12 inline int read() 13 { 14 int x=0,f=1;char ch=getchar(); 15 while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} 16 while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 17 return x*f; 18 } 19 20 typedef long long ll; 21 const int maxn = 1000000 + 10; 22 const int MOD = 1000000007;//const引用更快,宏定义也更快 23 const int INF = 1e9 + 7; 24 const double eps = 1e-6; 25 26 int n, m; 27 struct node 28 { 29 int fa;//记录父节点 30 int ch[2];//ch[0]表示左儿子 ch[1]表示右儿子 31 int val;//节点权值 32 int Size;//记录节点子树大小(包括该节点) 33 int cnt;//记录同样权值的元素个数 34 int mark;//记录反转区间标记(普通平衡树不用) 35 }t[maxn]; 36 int root = 0;//根节点 37 int cnt = 0;//当前节点数目 38 bool get(int x)//判断一个节点是左节点还是右节点 39 { 40 return t[t[x].fa].ch[1] == x;//右节点返回1 左节点返回0 41 } 42 void up(int x)//重新统计节点x的size 43 { 44 t[x].Size = t[t[x].ch[0]].Size + t[t[x].ch[1]].Size + t[x].cnt; 45 } 46 void Rotate(int x)//节点x与父节点旋转 47 { 48 int fa = t[x].fa, gfa = t[fa].fa; 49 int d1 = get(x), d2 = get(fa);//加上d1 d2均表示左儿子 50 t[fa].ch[d1] = t[x].ch[d1 ^ 1]; t[t[x].ch[d1 ^ 1]].fa = fa;//父节点的左儿子设置成左儿子的右儿子(双向设置) 51 t[gfa].ch[d2] = x; t[x].fa = gfa;//祖父节点的左儿子设置成父节点的左儿子(双向) 52 t[fa].fa = x; t[x].ch[d1^1] = fa;//左儿子的右儿子设置成父节点(双向) 53 up(fa); up(x); 54 } 55 void splay(int x, int goal)//x旋转到goal下面 56 { 57 while(t[x].fa != goal) 58 { 59 int fa = t[x].fa, gfa = t[fa].fa; 60 int d1 = get(x), d2 = get(fa); 61 if(gfa != goal) 62 { 63 if(d1 == d2)Rotate(fa);//同侧,先旋父节点(双旋) 64 else Rotate(x);//异侧 直接选 65 } 66 Rotate(x);//再向上旋一次 67 } 68 if(goal == 0)root = x; 69 } 70 void Insert(int val) 71 { 72 int node = root, fa = 0; 73 while(node && t[node].val != val) 74 fa = node, node = t[node].ch[t[node].val < val]; 75 if(node)t[node].cnt++;//节点存在 76 else 77 { 78 node = ++cnt; 79 if(fa)t[fa].ch[t[fa].val < val] = node; 80 t[node].fa = fa; 81 t[node].Size = t[node].cnt = 1; 82 t[node].val = val; 83 } 84 splay(node, 0);//将新节点旋到根来维护splay的子树 85 } 86 87 void pushdown(int x) 88 { 89 t[t[x].ch[0]].mark ^= 1; 90 t[t[x].ch[1]].mark ^= 1; 91 t[x].mark = 0; 92 swap(t[x].ch[0], t[x].ch[1]); 93 } 94 int kth(int k)//查询第k大的数 95 { 96 int node = root; 97 while(1) 98 { 99 if(t[node].mark)pushdown(node); 100 int son = t[node].ch[0]; 101 if(k <= t[son].Size)node = son; 102 else if(k > t[son].Size + t[node].cnt) 103 { 104 k -= t[son].Size + t[node].cnt; 105 node = t[node].ch[1]; 106 } 107 else return node;//返回编号 108 } 109 } 110 void work(int l, int r)//将l-1节点转到根节点 r+1节点转到根节点的右节点 111 //直接标记根节点的右节点的左节点就是区间l-r 112 { 113 int left = kth(l), right = kth(r); 114 splay(left, 0); 115 splay(right, left); 116 t[t[t[root].ch[1]].ch[0]].mark ^= 1; 117 } 118 119 void output(int x) 120 { 121 if(t[x].mark)pushdown(x); 122 if(t[x].ch[0])output(t[x].ch[0]); 123 if(t[x].val >= 1 && t[x].val <= n)printf("%d ", t[x].val); 124 if(t[x].ch[1])output(t[x].ch[1]); 125 } 126 127 int main() 128 { 129 int x, y; 130 Insert(INF); 131 Insert(-INF); 132 scanf("%d%d", &n, &m); 133 for(int i = 1; i <= n; i++)Insert(i); 134 while(m--) 135 { 136 scanf("%d%d", &x, &y); 137 work(x, y + 2);//由于有负无穷 等价于x-1节点 和 y+1节点 138 } 139 output(root); 140 puts(""); 141 return 0; 142 }
越努力,越幸运