hihocoder-第六十一周 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 题目分析:
题意分析
给定一个字符串s,以及对该字符串s的 m 个操作。
字符串s包含n个字符,下标为1..n。字符由'A'到'Z'构成,字符增加1表示该字符变为后续字符,比如
'A'增加1是'B','C'增加1是'D'。需要注意的是'Z'增加1是'A'。m个操作包含以下四种类型:
将字符串第i位到第j位设定为C。
比如当i=2,j=3,C='Z'时:
"ABCDEFG"变成"AZZDEFG"将字符串第i位到第j位增加K。
比如i=2,j=3,K=1时:
"ABCDEFG"变成"ACDDEFG"将字符串左边K位移至右边。
比如K=3时:
"ABCDEFG"变成"DEFGABC"从字符串第i位到第j位,依次增加1,2,...,j-i+1。
比如当i=2,j=3时:
"ABCDEFG"变成"ACEDEFG"
输出m个操作结束后的字符串s。
算法分析
本题需要根据每一次的操作去修改现在的s。若采用朴素的做法,每一次修改其最大代价为O(n),故总的时间复杂度为O(nm)。对于n=50000,m=50000的数据量来说,这样时间复杂度显然是不能够接受的。
仔细观察我们每一次的操作,其中CMD3是对整体进行了平移,CMD1,CMD2,CMD4都是针对i到j的一个区间进行操作。
首先我们来解决看似比较简单的CMD3操作:
若将整个字符串s看作环形,则线型的字符串是从起点指针SP开始顺时针将n个元素进行展开得到的。那么CMD3操作为顺时针移动该环的头指针。举个例子来说:

最开始头指针在1时,我们展开字符串为[1,2,3,4,5]。当执行CMD3 K=2操作后,起点指针SP移动到3的位置,此时展开的字符串为[3,4,5,1,2]。
符合CMD3操作的规则,并且起点指针SP的改变就是增加了K。
其中新字符串的第i~j位,对应的是原字符串第i+SP~j+SP位。
所以我们只需要维护一个SP指针,当执行CMD3操作时,改变SP的值。而对于其他操作的区间,只需要将区间从[i..j]变化到[i+SP..j+SP]即可。
需要注意的是,SP,i+SP,j+SP有可能会超过n。当超过n时,需要将其值减去n。
至此执行CMD3操作的时间复杂度降至O(1)。
接下来考虑CMD1,CMD2,CMD4。这三个操作均为区间上的操作,因此我们可以使用线段树来进行模拟。(在我们的Hiho一下第19期和第20期可以找到线段树的教程)
在那之前,我们需要对字符进行处理。从题目中我们知道当一个字符超过'Z'时,会直接变成'A'。所以我们可以直接考虑将'A'~'Z'与0~25对应起来。当一个字符增加了很多次K后,其实际表示的字符也就等于该值 mod 26。
构造线段树
构造线段树,主要是构造每个节点的数据域,使其能够记录我们需要的信息,同时在父节点和子节点之间能够进行信息的传递。根据本题的题意,我们构造的线段树其节点包含以下三个数据:
- same: 表示当前区间的字符是否相同,若相同则same等于该字符,否则same=-1
- add: 表示当前区间的增量,对应CMD2操作所增加的K
delta和 inc : 这两个变量是一组,其表示CMD4的操作。其含义为,该区间最左起第1个元素值增量为delta,此后每一个元素的增量比前一个多inc。即第2个元素的增量为delta+inc,第3个元素的增量为delta+inc+inc,...,第i个元素的增量为delta+inc*(i-1)。举个例子:
若我们对区间[1,3]进行了CMD4操作,实际的意义为s1+1,s[2]+2,s[3]+3。对于表示区间[1,3]的节点,其Delta=1,inc=1。
若我们对区间[1,3]进行了2次CMD4操作,实际意义为s1+2,s[2]+4,s[3]+6。则此时Delta=2,inc=2。而对于表示区间[2,3]的节点,其Delta=4,inc=2。因为该区间左起第1个元素为s[2]+4,故delta=4。
在本题中我们一开始便读入了字符串,该字符串的每一个字符对应了树的一个叶子节点。故我们一开始就需要建出整颗树,其代码:
// 该段代码我们采用的是数组模拟线段树
const int MAXN = ; struct sTreeNode {
int left, right;
int same, add;
int delta, inc;
int lch, rch;
} tree[ MAXN << ]; void createTree(int rt, int left, int right) {
tree[rt].left = left, tree[rt].right = right;
tree[rt].delta = tree[rt].step = ;
tree[rt].add = ; if (left == right) { // 叶子节点
tree[rt].base = str[ left ] - 'A';
tree[rt].lch = tree[rt].rch = ;
return ;
} // 非叶子节点
tree[rt].base = -;
tree[rt].lch = rt * , tree[rt].rch = rt * + ; int mid = (tree[rt].left + tree[rt].right) >> ;
createTree(tree[rt].lch, left, mid);
createTree(tree[rt].rch, mid + , right);
return ;
}更新线段树
在更新线段树时,需要注意更新区间可能会出现i+SP <= n并且j+SP大于n时,此时要将区间分为[i+SP..n]和[1..j+SP-n]两个部分单独处理。
更新线段树信息的update函数:
// rt表示当前节点
// left,right表示此次操作的区间
// key表示此次操作K或Delta
// type表示此次操作的类型
void update(int rt, int left, int right, int key, int type) {
if (!rt) return ;
if (tree[rt].right < left || tree[rt].left > right) return ;
if (left <= tree[rt].left && tree[rt].right <= right) {
// 当前节点区间完全包含于[left,right]
// 更新当前区间信息
...
} else {
// 当前节点区间不完全包含于[left,right],则需要让子区间来处理
// 传递当前区间的信息
... // 更新当前区间信息
... // 迭代处理
update(tree[rt].lch, left, right, key, type);
update(tree[rt].rch, left, right, key, type);
}
return ;
}若当前区间包含于[left,right],根据操作的不同我们进行如下的处理:
CMD1: 直接更新区间的same值,同时将add,delta和inc置为0 if (type == ) {
tree[rt].same = key;
tree[rt].delta = , tree[rt].inc = ;
tree[rt].add = ;
}CMD2: 累加到当前区间的add上 if (type == ) {
tree[rt].add += key;
}CMD4: 将新的delta和inc累加到当前区间的delta和inc上 if (type == ) {
tree[rt].delta += key + (tree[rt].left - left);
tree[rt].inc ++;
}
当需要对子区间进行处理时,我们需要将当前区间的信息传递下去,此时需要判断当前区间的same值:
// 传递当前区间的信息
int mid = (tree[rt].left + tree[rt].right) / ; if (tree[rt].base == -) {
// lch
tree[ tree[rt].lch ].delta += tree[rt].delta;
tree[ tree[rt].lch ].step += tree[rt].step;
tree[ tree[rt].lch ].add += tree[rt].add;
// rch
tree[ tree[rt].rch ].delta += tree[rt].delta + (mid - tree[rt].left + ) * tree[rt].step;
tree[ tree[rt].rch ].step += tree[rt].step;
tree[ tree[rt].rch ].add += tree[rt].add;
} else {
tree[ tree[rt].lch ].base = tree[ tree[rt].rch ].base = tree[rt].base;
tree[ tree[rt].lch ].delta = tree[rt].delta;
tree[ tree[rt].rch ].delta = tree[rt].delta + (mid - tree[rt].left + ) * tree[rt].step;
tree[ tree[rt].lch ].step = tree[ tree[rt].rch ].step = tree[rt].step;
tree[ tree[rt].lch ].add = tree[ tree[rt].rch ].add = tree[rt].add;
}当我们把当前区间的信息传递下去后,可以知道当前区间内的字符一定会发生改变,所以设置其same=1。同时由于当前区间的add,delta和inc信息已经传递下去,其本身的add,delta和inc设置为0:
// 更新当前区间信息
tree[rt].base = -;
tree[rt].delta = tree[rt].step = ;
tree[rt].add = ;产生新的字符串
在这一步我们需要对整个线段树进行一次遍历,将所有的信息传递到叶子节点,再根据叶子节点的值产生我们新的字符串。
int f[ MAXN ]; // 记录每个叶子节点的数值
void getResult(int rt) {
if (!rt) return ;
if (tree[rt].base != -) {
int delta = tree[rt].delta;
for (int i = tree[rt].left; i <= tree[rt].right; ++i)
f[i] = tree[rt].base + tree[rt].add + delta, delta += tree[rt].step;
} else {
int mid = (tree[rt].left + tree[rt].right) / ;
// lch
tree[ tree[rt].lch ].delta += tree[rt].delta;
tree[ tree[rt].lch ].step += tree[rt].step;
tree[ tree[rt].lch ].add += tree[rt].add;
// rch
tree[ tree[rt].rch ].delta += tree[rt].delta + (mid - tree[rt].left + ) * tree[rt].step;
tree[ tree[rt].rch ].step += tree[rt].step;
tree[ tree[rt].rch ].add += tree[rt].add; getResult(tree[rt].lch);
getResult(tree[rt].rch);
}
return ;
}此时得到的s并不是我们最后的结果,还需要根据SP的值来输出
void typeAns() {
for (int i = ; i < n; ++i)
printf("%c", (char) (f[(SP + i) % n] + 'A'));
printf("\n");
return ;
}#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <queue>
#include <map>
#define maxn 50000 + 100
using namespace std;
string ch;
char alpha[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
void dfs(int u, int v, int tt)
{
int res = ;
for(int i = u - ; i <= v - ; i++)
{
int tt = (ch[i] - 'A' + res) % ;
ch[i] = alpha[tt];
res++;
}
return;
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
cin >> ch;
int op;
int u, v, w;
char c[];
char cc[];
while(m--)
{
cin >> cc >> op;
if(op == )
{
scanf("%d %d %s", &u, &v, c);
for(int i = u - ; i <= v - ; i++)
{
ch[i] = c[];
}
//cout << ch << endl;
}
else if(op == )
{
scanf("%d %d %d", &u, &v, &w);
w = w % ;
for(int i = u - ; i <= v - ; i++)
{
int tt = (ch[i] - 'A' + w) % ;
ch[i] = alpha[tt];
}
// cout << ch << endl;
}
else if(op == )
{
scanf("%d", &w);
string ch1 = ch;
string s1 = ch.substr(, w);
string s2 = ch1.substr(w, n - w + );
ch = "";
ch = s2 + s1;
//cout << ch << endl;
}
else if(op == )
{
scanf("%d %d", &u, &v);
//printf("%d %d %d\n", u, v, w);
dfs(u, v, w);
// cout << ch << endl;
}
}
cout << ch << endl;
return ;
}TLE暴力了一次
根据题意线段树:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string.h>
#include <stdio.h>
#include <queue>
using namespace std;
#define maxn 50000 + 100
int len;
char alpha[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
int sp = ;
using namespace std;
char ch[maxn];
struct Tire
{
int same; ///表示当前区间的字符是否相同,若相同则same等于该字符,否则same=-1
int add; ///表示当前区间的增量,对应CMD2操作所增加的K
int delta; ///该区间最左起第1个元素值增量为delta
int inc; ///每一个元素的增量比前一个多
int val; ///当前的值
} tree[maxn << ];
void build(int left, int right, int root)
{
tree[root].same = -;
tree[root].add = ;
tree[root].delta = ;
tree[root].inc = ;
tree[root].val = ;
if(left == right)
{
return;
}
int mid = (left + right) >> ;
build(left, mid, root << );
build(mid + , right, root << | );
return;
}
void change(int& x, int y) {
x += y;
x %= ;
} void getid(int s, int e, int& s1, int& e1, int& s2, int& e2) {
s--;
e--;
s1 = s2 = e1 = e2 = -;
int t1 = sp + s, t2 = sp + e;
if(t1 < len && t2 < len) {
s1 = t1;
e1 = t2;
s2 = e2 = -;
}
else if(t1 < len && t2 >= len) {
t2 %= len;
s1 = t1;
e1 = len - ;
s2 = ;
e2 = t2;
}
else if(t1 >= len && t2 >= len) {
t1 %= len;
t2 %= len;
s1 = t1;
e1 = t2;
s2 = e2 = -;
}
s1++;
e1++;
s2++;
e2++;
}
void pushup(int left, int right, int root)
{
if(left == right)
{
return;
}
if(tree[root].same >= )
{
tree[root << ].same = tree[root << ].val = tree[root].same;
tree[root << | ].same = tree[root << | ].val = tree[root].same;
tree[root].same = -;
tree[root << ].add = tree[root << ].delta = tree[root << ].inc = ;
tree[root << | ].add = tree[root << | ].delta = tree[root << | ].inc = ;
}
if(tree[root].add >= )
{
change(tree[root << ].add, tree[root].add);
change(tree[root << | ].add, tree[root].add);
tree[root].add = ;
}
if(tree[root].delta >= )
{
change(tree[root << ].delta, tree[root].delta);
change(tree[root << ].inc, tree[root].inc);
change(tree[root << | ].delta, tree[root].delta + ((right - left) / + )*tree[root]. inc);
change(tree[root << | ].inc, tree[root].inc);
tree[root].delta = ;
tree[root].inc = ;
}
return;
} void update(int op, int L, int R, int root, int left, int right, int val)
{
pushup(left, right, root);
if(L <= left && right <= R)
{
if(op == )
{
change(tree[root].add, val);
}
else if(op == )
{
change(tree[root].inc, );
change(tree[root].delta, left - L + val);
}
else if(op == )
{
tree[root].same = val;
tree[root].val = val;
tree[root].add = ;
tree[root].delta = ;
tree[root].inc = ;
}
return;
}
int mid = (left + right) / ;
if(mid >= L) {
update(op, L, R, root << , left, mid, val);
}
if(R > mid) {
update(op, L, R, root << | , mid + , right, val);
}
return;
}
int query(int L, int R, int root, int left, int right)
{
pushup(left, right, root);
if(L <= left && R >= right)
{
return (tree[root].val + tree[root].add + tree[root].delta) % ;
}
int mid = (left + right) >> ;
if(L <= mid) {
return query(L, R, root << , left, mid);
}
if(R > mid) {
return query(L, R, root << | , mid + , right);
}
return ;
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
cin >> ch;
build(, n, );
for(int i = ; i < n; i++)
{
update(, i + , i + , , , n, ch[i] - 'A');
}
sp = ;
int op;
int u, v, w;
char c[];
char cc[];
int s1, e1, s2, e2;
len = n;
for(int jj = ; jj < m; jj++)
{
cin >> cc >> op;
if(op == )
{
cin >> u >> v >> c;
getid(u, v, s1, e1, s2, e2);
update(, s1, e1, , , n, c[] - 'A');
if(s2 != - && e2 != -) {
update(, s2, e2, , , n, c[] - 'A');
}
}
else if(op == )
{
cin >> u >> v >> w;
getid(u, v, s1, e1, s2, e2);
update(, s1, e1, , , n, w);
if(s2 != - && e2 != -) {
update(, s2, e2, , , n, w);
}
}
else if(op == )
{
scanf("%d", &w);
sp = sp + w;
sp = sp % n;
}
else if(op == )
{
cin >> u >> v;
getid(u, v, s1, e1, s2, e2);
update(, s1, e1, , , n, );
if(s2 != - && e2 != -)
{
update(, s2, e2, , , n, e1 - s1 + );
}
}
}
int tt = sp;
for(int i = ; i < n; i++)
{
printf("%c", 'A' + query(tt + , tt + , , , n));
tt++;
tt %= n;
}
return ;
}
hihocoder-第六十一周 Combination Lock的更多相关文章
- hihocoder #1058 Combination Lock
传送门 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Finally, you come to the interview room. You know that a ...
- 201521123061 《Java程序设计》第十一周学习总结
201521123061 <Java程序设计>第十一周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 本周学习的是如何解决多线程访问中的互斥 ...
- 201521123072《java程序设计》第十一周学习总结
201521123072<java程序设计>第十一周学习总结 1. 本周学习总结 2. 书面作业 本次PTA作业题集多线程 互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问 ...
- 201521123038 《Java程序设计》 第十一周学习总结
201521123038 <Java程序设计> 第十一周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多 ...
- 201521123122 《java程序设计》第十一周学习总结
## 201521123122 <java程序设计>第十一周实验总结 ## 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 其实这周也没讲多少内容,所 ...
- 201621123040《Java程序设计》第十一周学习总结
1.本周学习总结 1.1以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2.书面作业 2.1源代码阅读:多线程程序BounceThread 2.1.1BallRunnable类有什么用?为什 ...
- 杨其菊/常惠琢《面向对象程序设计(java)》第十一周学习总结
<面向对象程序设计>第十一周学习总结 第一部分:理论知识 JAVA的集合框架 JAVA的集合框架实现对各种数据结构的封装,以降低对数据管理与处理的难度. 所谓框架就是一个类库的集合,框 ...
- 20172325 2017-2018-2 《Java程序设计》第十一周学习总结
20172325 2017-2018-2 <Java程序设计>第十一周学习总结 教材学习内容总结 Android简介 Android操作系统是一种多用户的Linux系统,每个应用程序作为单 ...
- 第十一周PSP&进度条
PSP 一.表格: D日期 C类型 C内容 S开始时间 E结束时间 I时间间隔 T净时间(mins) 预计花费时间(mins) 11月24号 站立会议 分配任务&设计final方案 1 ...
随机推荐
- Action中使用Json
1.前台页面中的ajax: //根据部门查询该部门下的用户列表 function doSelectDept(){ //1.获取部门 var dept = $("#toCompDept opt ...
- 【bzoj2815】[ZJOI2012]灾难 拓扑排序+倍增LCA
题目描述(转自洛谷) 阿米巴是小强的好朋友. 阿米巴和小强在草原上捉蚂蚱.小强突然想,果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难. 学过生物 ...
- 附录A培训实习生-面向对象基础构造方法和带参数的构造方法(2)
构造方法,又叫构造函数,其实就是对类进行实例化.构造方法与类同名,无返回值,也不需要void,在new时候调用.也就是说,就是调用构造方法的时候. 所有类都有构造方法,如果你不编码则系统默认生成空的的 ...
- Partial Class部分类
Partial Class,部分类 或者分布类.顾名思义,就是将一个类分成多个部分.比如说:一个类中有3个方法,在VS 2005将该类中3个方法分别存放在3个不同的.cs文件中.这样做的好处:1.一个 ...
- [CF327E]Axis Walking([洛谷P2396]yyy loves Maths VII)
题目大意:给一个长度为$n(1\leqslant n\leqslant24)$的序列$S$和$k(0\leqslant k\leqslant2)$个数. 求有多少种$S$的排列方式使得其任何一个前缀和 ...
- BZOJ1115 [POI2009]石子游戏Kam 【博弈论——阶梯游戏】
题目 有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数.两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件谁没有石子可移时输掉游戏.问先手是否必胜. ...
- 洛谷 P2529 [SHOI2001]击鼓传花 解题报告
P2529 [SHOI2001]击鼓传花 题意:求出\(n!\)末尾最后一位非0数字 数据范围:\(n<=10^{100}\) 我们从简单的开始考虑 1.显然,\(n!\)可以被这么表示 \(n ...
- 关于label标签的作用
label标签的定义和用法: <label> 标签为 input 元素定义标注(标记). label 元素不会向用户呈现任何特殊效果.不过,它为鼠标用户改进了可用性.如果您在 label ...
- makefile使用笔记(一)入门
By francis_hao Mar 2,2017 makefile makefile一个很简单的例子如下,该实例完成在执行make时,将main.c编译成可执行文件main的功能. 各项的含义 ...
- [bzoj 2818]欧拉函数
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2818 枚举最大公约数,对于每一个质数p,只需要求出1<=x,y<=(n/p)范 ...