hihocoder #1058 Combination Lock
描述
Finally, you come to the interview room. You know that a Microsoft interviewer is in the room though the door is locked. There is a combination lock on the door. There are N rotators on the lock, each consists of 26 alphabetic characters, namely, 'A'-'Z'. You need to unlock the door to meet the interviewer inside. There is a note besides the lock, which shows the steps to unlock it.
Note: There are M steps totally; each step is one of the four kinds of operations shown below:
Type1: CMD 1 i j X: (i and j are integers, 1 <= i <= j <= N; X is a character, within 'A'-'Z')
This is a sequence operation: turn the ith to the jth rotators to character X (the left most rotator is defined as the 1st rotator)
For example: ABCDEFG => CMD 1 2 3 Z => AZZDEFG
Type2: CMD 2 i j K: (i, j, and K are all integers, 1 <= i <= j <= N)
This is a sequence operation: turn the ith to the jth rotators up K times ( if character A is turned up once, it is B; if Z is turned up once, it is A now. )
For example: ABCDEFG => CMD 2 2 3 1 => ACDDEFG
Type3: CMD 3 K: (K is an integer, 1 <= K <= N)
This is a concatenation operation: move the K leftmost rotators to the rightmost end.
For example: ABCDEFG => CMD 3 3 => DEFGABC
Type4: CMD 4 i j(i, j are integers, 1 <= i <= j <= N):
This is a recursive operation, which means:
If i > j: Do Nothing Else: CMD 4 i+1 j CMD 2 i j 1For example: ABCDEFG => CMD 4 2 3 => ACEDEFG
输入
1st line: 2 integers, N, M ( 1 <= N <= 50000, 1 <= M <= 50000 )
2nd line: a string of N characters, standing for the original status of the lock.
3rd ~ (3+M-1)th lines: each line contains a string, representing one step.
输出
One line of N characters, showing the final status of the lock.
提示
Come on! You need to do these operations as fast as possible.
- 样例输入
-
7 4 ABCDEFG CMD 1 2 5 C CMD 2 3 7 4 CMD 3 3 CMD 4 1 7
- 样例输出
- HIMOFIN
Analysis:
Implementation:
#include <bits/stdc++.h> using namespace std; const int N(5e4+5); int same[N<<2], rot[N<<2], delta[N<<2], inc[N<<2]; char s[N]; void build(int id, int L, int R){ if(L==R){same[id]=s[L]-'A'; return;} int mid=(L+R)>>1; same[id]=-1; build(id<<1, L, mid); build(id<<1|1, mid+1, R); } void CLEAR(int id, int v){ same[id]=v, rot[id]=delta[id]=inc[id]=0; } void push_rot(int s, int f){ rot[s]+=rot[f], rot[s]%=26; } void push_inc(int s, int f, int d){ delta[s]+=d, inc[s]+=inc[f], delta[s]%=26, inc[s]%=26; } void push_down(int id, int L, int R){ int ls=id<<1, rs=ls|1; if(~same[id]) CLEAR(ls, same[id]), CLEAR(rs, same[id]), same[id]=-1; if(rot[id]) push_rot(ls, id), push_rot(rs, id), rot[id]=0; int mid=(R+L)>>1, t=delta[id]+(mid+1-L)*inc[id]; push_inc(ls, id, delta[id]), push_inc(rs, id, t%26), delta[id]=inc[id]=0; } int query(int id, int L, int R, int p){ if(~same[id]) return (same[id]+rot[id]+delta[id]+(p-L)*inc[id])%26; push_down(id, L, R); //error-prone int mid=(L+R)>>1; if(p<=mid) return query(id<<1, L, mid, p); return query(id<<1|1, mid+1, R, p); } void SET(int id, int L, int R, int l, int r, int v){ if(l<=L && R<=r){ CLEAR(id, v); return; } push_down(id, L, R); int mid=(L+R)>>1; if(l<=mid) SET(id<<1, L, mid, l, r, v); if(r>mid) SET(id<<1|1, mid+1, R, l, r, v); } void ROTATE(int id, int L, int R, int l, int r, int v){ if(l<=L && R<=r){rot[id]+=v, rot[id]%=26; return;} push_down(id, L, R); int mid=(L+R)>>1; if(l<=mid) ROTATE(id<<1, L, mid, l, r, v); if(r>mid) ROTATE(id<<1|1, mid+1, R, l, r, v); } void INC_SHIFT(int id, int L, int R, int l, int r, int v){ if(l<=L && R<=r){ delta[id]+=v+L-l; //error-prone inc[id]+=1; delta[id]%=26, inc[id]%=26; return; } push_down(id, L, R); int mid=(L+R)>>1; if(l<=mid) INC_SHIFT(id<<1, L, mid, l, r, v); //error-prone if(r>mid) INC_SHIFT(id<<1|1, mid+1, R, l, r, v); } int main(){ int n, m; cin>>n>>m>>s; build(1, 0, n-1); char ch; int shift=0; //number of left shift for(int t, l, r, k; m--; ){ scanf("%*s%d", &t); if(t!=3) cin>>l>>r, l--, r--, l=(l+shift)%n, r=(r+shift)%n; if(t==1){ cin>>ch, k=ch-'A'; //cin ignores leading spaces. if(l<=r) SET(1, 0, n-1, l, r, k); else SET(1, 0, n-1, l, n-1, k), SET(1, 0, n-1, 0, r, k); } else if(t==2){ cin>>k; if(l<=r) ROTATE(1, 0, n-1, l, r, k); else ROTATE(1, 0, n-1, l, n-1, k), ROTATE(1, 0, n-1, 0, r, k); } else if(t==3){ cin>>k, shift+=k, shift%=n; } else{ if(l<=r) INC_SHIFT(1, 0, n-1, l, r, 1); else INC_SHIFT(1, 0, n-1, l, n-1, 1), INC_SHIFT(1, 0, n-1, 0, r, n-l+1); } } for(int i=0; i<n; i++) putchar(query(1, 0, n-1, (i+shift)%n)+'A'); puts(""); return 0; }
实现细节:
这道题代码量相对大一些,而且有些地方容易想不清楚。
先总结一下线段树:
线段树是用来维护区间上的修改(亦称 更新/modify/update)与查询(query)的。修改与查询都可分成两类:点修改,区间修改;点查询,区间查询。
其中区间修改往往要用到 lazy-tag 技巧。线段树节点维护的所有atrribute都是关于这个节点所对应的区间的,广义而言,都可看作区间的函数
\[f([L, R])\] 这些 attribute 记录的信息可分为两类,一类是该区间的某种属性(properties),另一类是对此区间(已经)进行的某些操作(operations),或者说该区间经历 (expierenced)的某些操作。
再说说这道题的实现:
线段树的每个节点所需的 atrribute,除了 Analysis 中提到的 delta, inc(用来记录该区间经历的CMD 4操作)之外,还有
- same,用来记录该区间经历的CMD 1操作,我们用0~25代表'A'~'Z';
- rot,用来记录该区间所经历的CMD 2操作;
CMD 1操作会将该区间已经历的所有其他操作全部覆盖(清空)。