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 }

 

NOIP普及组、提高组培训,有意可加微信fu19521308684
posted @ 2018-09-19 22:29  _努力努力再努力x  阅读(98)  评论(0编辑  收藏  举报