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 ...
随机推荐
- HDU 4582 DFS spanning tree(DFS+贪心)(2013ACM-ICPC杭州赛区全国邀请赛)
Problem Description Consider a Depth-First-Search(DFS) spanning tree T of a undirected connected gra ...
- Java项目启动时候报Neither the JAVA_HOME nor the JRE_HOME environment variable is defined 解决办法
今天在发布Java项目的时候又遇到 Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At leas ...
- Mac上基于hexo+GitHub搭建个人博客(一)
原文地址: http://fanjiajia.cn/2018/11/23/Mac%E4%B8%8A%E5%9F%BA%E4%BA%8Ehexo+GitHub%E6%90%AD%E5%BB%BA%E4% ...
- C语言单元测试
转自http://blog.csdn.net/colin719/article/details/1420583 对于敏捷开发来说,单元测试必不可少,对于Java开发来说,JUnit非常好,对于C++开 ...
- 【Python】python之Character string
1.python字符串 字符串是 Python 中最常用的数据类型.我们可以使用引号('或")来创建字符串,l Python不支持单字符类型,单字符也在Python也是作为一个字符串使用. ...
- 搭建ELK 6
ELK 6.2.4搭建 https://www.cnblogs.com/harvey2017/p/8922164.html 开源实时日志分析ELK平台能够完美的解决我们上述的问题,ELK由Elasti ...
- JavaScript正则表达式大全
一.校验数字的表达式 1 数字:^[0-9]*$ 2 n位的数字:^\d{n}$ 3 至少n位的数字:^\d{n,}$ 4 m-n位的数字:^\d{m,n}$ 5 零和非零开头的数字:^(0|[1-9 ...
- hdu 1787 GCD Again (欧拉函数)
GCD Again Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- 【题解】NOIP2016愤怒的小鸟
一眼n<=18状压dp……方程什么的都很显然,枚举两只小鸟,再将这条抛物线上的小鸟抓出来就好啦.只是这样O(n^3)的dp必然是要TLE的,我一开始这样交上去显然跑得巨慢无比,后来转念一想:面对 ...
- [bzoj1018] [SHOI2008]堵塞的交通
题目描述 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个22行CC列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所 ...