传送门

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

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 1

For 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:

CMD 1CMD 2是裸的线段树。CMD 4看起来很“吓人”,仔细分析一下不难发现CMD 4 i j是将密码锁的i位旋转(turn up)1次,第i+1位旋转2次,第i+1位旋转3次,...这也可以用线段树维护。 为了表述方便,我们用delta[i]表示位置i上由CMD 4造成的turn up次数。做法是将线段树的每个节点(每个节点都代表一个区间(L, R))增添两个attribute:一个是delta,用来记录delta[L];另一个是inc,用来记录该区间内delta[i+1]-delta[i]的值。应当注意到当对线段树上的某个区间(节点)进行第1次CMD 4操作后,其delta值是1,inc值是1;接着进行第2次操作后,其delta值是2,inc值是2。下面考虑CMD 3CMD 3 k 实际上是将字符串(即尚在构造中的密码)循环左移k位,比如对"ABCDEFG"进行CMD 3 3操作后,它变成"DEFGABC"。这种操作似乎是和线段树的精神相违背的,其实不然。线段树维护始终都是未经过移位操作的串,若原串已经循环累计左移s位了,我们只要将对应于当前(新)串的操作区间循环右移s位(注意:右移后[L, R]可能分裂为两段),之后再放到线段树里更新。

Implementation:

先贴代码,以后再填坑。
#include <bits/stdc++.h>
using namespace std; const int N(5e4+);
int same[N<<], rot[N<<], delta[N<<], inc[N<<];
char s[N]; void build(int id, int L, int R){
if(L==R){same[id]=s[L]-'A'; return;}
int mid=(L+R)>>;
same[id]=-;
build(id<<, L, mid);
build(id<<|, mid+, R);
} void CLEAR(int id, int v){
same[id]=v, rot[id]=delta[id]=inc[id]=;
} void push_rot(int s, int f){
rot[s]+=rot[f], rot[s]%=;
} void push_inc(int s, int f, int d){
delta[s]+=d, inc[s]+=inc[f], delta[s]%=, inc[s]%=;
} void push_down(int id, int L, int R){
int ls=id<<, rs=ls|;
if(~same[id]) CLEAR(ls, same[id]), CLEAR(rs, same[id]), same[id]=-;
if(rot[id]) push_rot(ls, id), push_rot(rs, id), rot[id]=;
int mid=(R+L)>>, t=delta[id]+(mid+-L)*inc[id];
push_inc(ls, id, delta[id]), push_inc(rs, id, t%), delta[id]=inc[id]=;
} int query(int id, int L, int R, int p){
if(~same[id]) return (same[id]+rot[id]+delta[id]+(p-L)*inc[id])%;
push_down(id, L, R); //error-prone
int mid=(L+R)>>;
if(p<=mid) return query(id<<, L, mid, p);
return query(id<<|, mid+, 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)>>;
if(l<=mid) SET(id<<, L, mid, l, r, v);
if(r>mid) SET(id<<|, mid+, 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]%=; return;}
push_down(id, L, R);
int mid=(L+R)>>;
if(l<=mid) ROTATE(id<<, L, mid, l, r, v);
if(r>mid) ROTATE(id<<|, mid+, 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]+=;
delta[id]%=, inc[id]%=;
return;
}
push_down(id, L, R);
int mid=(L+R)>>;
if(l<=mid) INC_SHIFT(id<<, L, mid, l, r, v); //error-prone
if(r>mid) INC_SHIFT(id<<|, mid+, R, l, r, v);
} int main(){
int n, m;
cin>>n>>m>>s;
build(, , n-);
char ch;
int shift=; //number of left shift
for(int t, l, r, k; m--; ){
scanf("%*s%d", &t);
if(t!=) cin>>l>>r, l--, r--, l=(l+shift)%n, r=(r+shift)%n;
if(t==){
cin>>ch, k=ch-'A'; //cin ignores leading spaces.
if(l<=r) SET(, , n-, l, r, k);
else SET(, , n-, l, n-, k), SET(, , n-, , r, k);
}
else if(t==){
cin>>k;
if(l<=r) ROTATE(, , n-, l, r, k);
else ROTATE(, , n-, l, n-, k), ROTATE(, , n-, , r, k);
}
else if(t==){
cin>>k, shift+=k, shift%=n;
}
else{
if(l<=r) INC_SHIFT(, , n-, l, r, );
else INC_SHIFT(, , n-, l, n-, ), INC_SHIFT(, , n-, , r, n-l+);
}
}
for(int i=; i<n; i++) putchar(query(, , n-, (i+shift)%n)+'A');
puts("");
return ;
}

实现细节:

这道题代码量相对大一些,而且有些地方容易想不清楚。

先总结一下线段树:

线段树是用来维护区间上的修改(亦称 更新/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操作会将该区间已经历的所有其他操作全部覆盖(清空)。

 
 
 
 
 
 
 

hihocoder #1058 Combination Lock的更多相关文章

  1. Combination Lock

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Finally, you come to the interview room. You know that a Micr ...

  2. 贪心 Codeforces Round #301 (Div. 2) A. Combination Lock

    题目传送门 /* 贪心水题:累加到目标数字的距离,两头找取最小值 */ #include <cstdio> #include <iostream> #include <a ...

  3. Codeforces Round #301 (Div. 2) A. Combination Lock 暴力

    A. Combination Lock Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/540/p ...

  4. Hiho----微软笔试题《Combination Lock》

    Combination Lock 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Finally, you come to the interview room. You ...

  5. CF #301 A :Combination Lock(简单循环)

    A :Combination Lock 题意就是有一个密码箱,密码是n位数,现在有一个当前箱子上显示密码A和正确密码B,求有A到B一共至少需要滚动几次: 简单循环:

  6. hihocoder-第六十一周 Combination Lock

    题目1 : Combination Lock 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Finally, you come to the interview roo ...

  7. A - Combination Lock

    Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Description Scroog ...

  8. HDU 3104 Combination Lock(数学题)

    题目链接:http://acm.hdu.edu.cn/showproblem.php? pid=3104 Problem Description A combination lock consists ...

  9. 洛谷 P2693 [USACO1.3]号码锁 Combination Lock

    P2693 [USACO1.3]号码锁 Combination Lock 题目描述 农夫约翰的奶牛不停地从他的农场中逃出来,导致了很多损害.为了防止它们再逃出来,他买了一只很大的号码锁以防止奶牛们打开 ...

随机推荐

  1. Hill密码

    希尔密码(Hill Password)是运用基本矩阵论原理的替换密码,由Lester S. Hill在1929年发明.每个字母当作26进制数字:A=, B=, C=... 一串字母当成n维向量,跟一个 ...

  2. static,静态关键字的详解

    一,使用static声明属性 class Person{ // 定义Person类 String name ; // 定义name属性,暂时不封装 int age ; // 定义age属性,暂时不封装 ...

  3. git 添加文件

    git 添加文件三步骤 git add filename git commit -m 'remarks' git push origin master

  4. UICollectionView使用

    本文原文 原文转自 1.1. Collection View 全家福: UICollectionView, UITableView, NSCollectionView n   不直接等效于NSColl ...

  5. PHP 魔术引号

    1.魔术引号的作用是什么? ​ 魔术引号设计的初衷是为了让从数据库或文件中读取数据和从请求中接收参数时,对单引号.双引号.反斜线.NULL加上一个一个反斜线进行转义,这个的作用跟addslashes( ...

  6. Delphi的基于接口(IInterface)的多播监听器模式(观察者模式 )

    本文来自:http://www.cnblogs.com/hezihang/p/6083555.html Delphi采用接口方式设计模块,可以降低模块之间的耦合,便于扩展和维护.本文提供一个实现基于接 ...

  7. 推荐一款开源的C#TCP通讯框架

    原来收费的TCP通讯框架开源了,这是一款国外的开源TCP通信框架,使用了一段时间,感觉不错,介绍给大家 框架名称是networkcomms 作者开发了5年多,目前已经停止开发,对于中小型的应用场景,够 ...

  8. sudo权限添加 和 rpm、deb之名词解释

    sudo权限添加: 刚开始用Center_os Linux操作系统,想装个输入法,搜了一下,看到linux下的搜狗输入法(帖子链接)下载下来的文件的扩展名是.deb,直接用帖子上的一个命令: sudo ...

  9. MySQL基础 - 内置函数

    Concat() 用于连接字段,一般DBMS使用+或者||. ex: 注意:上图中新检索出来的列名为'CONCAT(id, '->', name)'(实际上没有列名),这样虽然不影响在MySQL ...

  10. 【转】Python Twisted介绍

    Python Twisted介绍 作者:Jessica McKellar 原文链接 Twisted是用Python实现的基于事件驱动的网络引擎框架.Twisted诞生于2000年初,在当时的网络游戏开 ...