YZOJ P3367 魔术帽游戏
时间限制:1000MS 内存限制:262144KB
难度:\(6.0\) 出题人:zzx
-
题目描述
有 \(n\) 顶外形相同的魔术帽和一个魔术球,每次游戏开始前,魔术帽会被倒扣放置排成一排,这些魔术帽从左到右依次编号为 \(1, 2, \cdots , n\) 。
每一局游戏,魔术球会被放在其中一顶魔术帽底下,然后进行若干次交换,每次交换时可以选出两顶魔术帽,交换它们的位置。整个过程对于小朋友们而言都是可见的。交换结束后,小朋友们可以打开魔术帽,正确找到魔术球则游戏胜利。
为了进行多局游戏,现有一个长度为 \(m\) 的操作序列 \((a_1,b_1), (a_2,b_2), \cdots ,(a_m,b_m)\),其中 \((a_i,b_i)\) 表示反转 \(a_i\) 号和 \(b_i\) 号魔术帽之间的魔术帽的顺序(如原来魔术帽从左到右为 \(a,b,c,d,e,f,g\),则操作 \((3,6)\) 进行后顺序变为 \(a,b,f,e,d,c,g\) 。之后,小朋友们会玩 \(q\) 局游戏。其中,第 \(j\) 轮游戏,魔术球会被放在 \(x_j\) 号魔术帽下,然后进行操作序列中 \([l_j,r_j]\) 这个片段,即依次进行反转操作 \((a_{l_j},b_{l_j}),(a_{l_j+1},b_{l_j+1}), \cdots ,(a_{r_j},b_{r_j})\) 。请求出每次游戏反转操作结束时,魔术球位于在哪一顶魔术帽下。注意:这里的魔术帽编号始终是按照位置从左到右编号的,即每次交换魔术帽之后,所有魔术帽会按照从左到右的顺序重新编号为 \(1,2,\cdots,n\) 。
-
输入格式
第一行有三个整数 \(n,m,q\),其中 \(n\) 代表魔术帽的数量,\(m\) 代表操作序列的长度,\(q\) 代表游戏次数。
接下来 \(m\) 行,其中第 \(i\) 行两个整数 \(a_i,b_i\),表示操作序列的第 \(i\) 项。接下来 \(q\) 行,其中第 \(j\) 行三个正整数 \(x_j,l_j,r_j\),表示第 \(j\) 局游戏。保证 \(1 \leq a_i \leq b_i \leq n\),\(1 \leq x_j \leq n\),\(1 \leq l_j \leq r_j \leq m\) 。
-
输出格式
输出 \(q\) 行,每行一个整数,第 \(j\) 行的整数表示第 \(j\) 局游戏的交换结束后,魔术球所在的魔术帽编号。
-
样例输入
1 2 3 4 5 6 7 8 9 |
5 5 3 2 4 1 4 1 5 3 4 1 2 2 1 3 1 2 4 2 3 5 |
-
样例输出
1 2 3 |
5 2 3 |
-
数据规模与约定
\(10\%\):\(n=2\) 。
\(40\%\):\(n \leq 15\) 。
\(100\%\):\(1 \leq n,m,q \leq 100,000\) 。
先不考虑交换区间的操作的话,可以做到线性复杂度处理。
首先考虑对所有的查询离线,然后分别把这个查询挂到操作区间的左端点和右端点。由于每次交换魔术帽之后,所有魔术帽会按照从左到右的顺序重新编号,所以我们对于每局游戏只需要记住球一开始放在哪一个魔术帽,结束时这个魔术帽在哪就行了。
扫过一遍操作序列,若是遇到一个点为一个查询的左端点,就记录下当前查询位置上的编号,遇到右端点时找到这个编号现在在哪一个位置,就是这个询问的答案了。
由于有翻转整个区间的操作,使用平衡树优化。
值得注意的是(会写 Splay 的可以走了orz),使用无旋 Treap 的时候,由于有区间操作,必须按照子树大小
split() ,所以在找某一节点的 order
时不能直接分治。其实只要在
PushUp() 的时候顺便记录一下每个节点的父节点,然后查询的时候不断往上跳,若这个点为它父亲节点的右儿子那么说明它的 order
加上左子树大小加 1。不要忘了
PushDown() 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #include <ctime> #ifdef ONLINE_JUDGE char __B[1<<15],*__S=__B,*__T=__B; #define getchar() (__S==__T&&(__T=(__S=__B)+fread(__B,1,1<<15,stdin),__S==__T)?EOF:*__S++) #endif inline int getnum() { register char c=0; while(!(c>='0' && c<='9')) c=getchar(); register int a=0; while(c>='0' && c<='9') { a*=10;a+=c-'0'; c=getchar(); } return a; } int g1head[105050],g1next[205050],g1node[205050],g1cnt; int g2head[105050],g2next[205050],g2node[205050],g2cnt; #define g1insertLine(_s,_t) \ g1node[++g1cnt]=_t,g1next[g1cnt]=g1head[_s],g1head[_s]=g1cnt; #define g2insertLine(_s,_t) \ g2node[++g2cnt]=_t,g2next[g2cnt]=g2head[_s],g2head[_s]=g2cnt; int N; int a[105050],b[105050],x[105050]; int ansnode[105050],ans[105050]; unsigned _seed,_ret; #define rnd() (_ret=_seed,_seed=_seed*7+23,_ret) int root,cnt,size[105050],lson[105050],rson[105050],val[105050],pri[105050],father[105050]; bool rev[105050]; inline int CreateNode(register int v) { size[++cnt]=1; val[cnt]=v; pri[cnt]=rnd(); return cnt; } inline void PushUp(register int x) { size[x]=size[lson[x]]+size[rson[x]]+1; father[lson[x]]=father[rson[x]]=x; (x==root ? father[x]=0 : 0); } inline void PushDown(register int x) { rev[x]^=1; lson[x]^=rson[x]^=lson[x]^=rson[x]; rev[lson[x]]^=1,rev[rson[x]]^=1; } inline int MergeTree(register int x,register int y) { if(!x || !y) return x^y; if(rev[x]) PushDown(x); if(rev[y]) PushDown(y); if(pri[x] < pri[y]) { rson[x]=MergeTree(rson[x],y); return PushUp(x),x; } else { lson[y]=MergeTree(x,lson[y]); return PushUp(y),y; } } inline void SplitTreeByOrder(register int o,register int k,int&x,int&y) { if(!o) { x=y=0; return; } if(rev[o]) PushDown(o); if(k <= size[lson[o]]) y=o,SplitTreeByOrder(lson[o],k,x,lson[o]); else x=o,SplitTreeByOrder(rson[o],k-size[lson[o]]-1,rson[o],y); PushUp(o); } inline void SegmentReverse(register int l,register int r) { int a=0,b=0,c=0,d=0; SplitTreeByOrder(root,r,a,b); SplitTreeByOrder(a,l-1,c,d); rev[d]^=1; root=MergeTree(MergeTree(c,d),b); } inline int OrderOfKey(register int v) { static int stk[105050]; register int top=0; for(register int x=v;father[x];x=father[x]) stk[++top]=x; if(rev[root]) PushDown(root); for(register int i=top;i>=1;i--) if(rev[stk[i]]) PushDown(stk[i]); register int s=size[lson[v]]+1; for(register int x=v;father[x];x=father[x]) if(x == rson[father[x]]) s+=size[lson[father[x]]]+1; return s; } inline int ValueOfOrder(register int x,register int k) { if(!x) return -1; if(rev[x]) PushDown(x); if(k == size[lson[x]]+1) return val[x]; if(k <= size[lson[x]]) return ValueOfOrder(lson[x],k); else return ValueOfOrder(rson[x],k-size[lson[x]]-1); } inline int BuildTree(register int l,register int r) { static int stk[105050]; register int top=0; for(register int i=l;i<=r;i++) { register int x=CreateNode(i),last=0; while(top && pri[stk[top]] > pri[x]) PushUp(stk[top]),last=stk[top],stk[top--]=0; if(top) rson[stk[top]]=x; lson[x]=last,stk[++top]=x; } while(top) PushUp(stk[top--]); return stk[1]; } int main() { srand(time(NULL));_seed=(unsigned)rand()*rand(); register int N=getnum(),M=getnum(),Q=getnum(); ::N=N; for(register int i=1;i<=M;i++) a[i]=getnum(),b[i]=getnum(); for(register int i=1;i<=Q;i++) { x[i]=getnum(); register int l=getnum(),r=getnum(); g1insertLine(l,i);g2insertLine(r,i); } root=BuildTree(1,N); //printf("%d\n",0); //printf("%d %d %d %d %d\n",ValueOfOrder(root,1),ValueOfOrder(root,2),ValueOfOrder(root,3),ValueOfOrder(root,4),ValueOfOrder(root,5)); //printf("pos: %d %d %d %d %d\n",OrderOfKey(1),OrderOfKey(2),OrderOfKey(3),OrderOfKey(4),OrderOfKey(5)); for(register int i=1;i<=M;i++) { for(register int j=g1head[i];j;j=g1next[j]) ansnode[j]=ValueOfOrder(root,x[g1node[j]]); SegmentReverse(a[i],b[i]); //printf("%d\n",i); //printf("%d %d %d %d %d\n",ValueOfOrder(root,1),ValueOfOrder(root,2),ValueOfOrder(root,3),ValueOfOrder(root,4),ValueOfOrder(root,5)); //printf("pos: %d %d %d %d %d\n",OrderOfKey(1),OrderOfKey(2),OrderOfKey(3),OrderOfKey(4),OrderOfKey(5)); for(register int j=g2head[i];j;j=g2next[j]) ans[g2node[j]]=OrderOfKey(ansnode[g2node[j]]); } for(register int i=1;i<=Q;i++) printf("%d\n",ans[i]); return 0; } |