题目描述

小南有一套可爱的玩具小人,它们各有不同的职业。
有一天,这些玩具小人把小南的眼镜藏了起来。小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外,如下图:

这时 `singer` 告诉小南一个谜题:「眼镜藏在我左数第 3 个玩具小人的右数第 1 个玩具小人的左数第 2 个玩具小人那里。」

小南发现,这个谜题中玩具小人的朝向非常关键, 因为朝内和朝外的玩具小人的左右方向是相反的:面朝圈内的玩具小人,它的左边是顺时针方向,右边是逆时针方向;而面向圈外的玩具小人,它的左边是逆时针方向,右边是顺时针方向。
小南一边艰难地辨认着玩具小人,一边数着:
`singer` 朝内,左数第 3 个是 `archer`。
`archer` 朝外,右数第 1 个是 `thinker`。
`thinker` 朝外,左数第 2 个是 `writer`。
所以眼镜藏在 `writer` 这里!
虽然成功找回了眼镜,但小南并没有放心。如果下次有更多的玩具小人藏他的眼镜,或是谜题的长度更长,他可能就无法找到眼镜了。所以小南希望你写程序帮他解决类似的谜题。这样的谜题具体可以描述为:
有 n 个玩具小人围成一圈,已知它们的职业和朝向。现在第 1 个玩具小人告诉小南一个包含 m 条指令的谜题。其中第 i 条指令形如「左数/右数第 si 个玩具小人」。你需要输出依次数完这些指令后,到达的玩具小人的职业。

输入描述:

输入的第一行包含两个正整数 n, m,表示玩具小人的个数和指令的条数。
接下来 n 行,每行包含一个整数和一个字符串,以逆时针为顺序给出每个玩具小人的朝向和职业。其中 0 表示朝向圈内,1 表示朝向圈外。保证不会出现其他的数。字符串长度不超过 10 且仅由小写字母构成,字符串不为空,并且字符串两两不同。整数和字符串之问用一个空格隔开。
接下来 m 行,其中第 i 行包含两个整数 ai, si,表示第 i 条指令。若 ai = 0,表示向左数 si 个人;若 ai = 1,表示向右数 si 个人。保证 ai 不会出现其他的数。1 ≤ si < n。

输出描述:

输出一个字符串,表示从第一个读入的小人开始,依次数完 m 条指令后到达的小人的职业。

示例1

输入
7 3
0 singer
0 reader
0 mengbier 
1 thinker
1 archer
0 writer
1 mogician 
0 3
1 1
0 2
输出
writer
说明
这组数据就是「题目描述」中提到的例子。

示例2

输入
10 10
1 C
0 r
0 P
1 d
1 e
1 m
1 t
1 y
1 u
0 V
1 7
1 1
1 4
0 5
0 3
0 1
1 6
1 2
0 8
0 4
输出
y
说明
这个数据中只有 t = 2 与上个数据不同。只需在每行都改为每两个数输出一个数即可。
虽然第一行最后有一个 6 没有被输出,但是第二行仍然要重新从第二个数再开始输出。

备注

1 ≤ n, m ≤ 100000

解答

实际上,这题挺简单的。

但是方向感不强的同学(比如说我)刚开始做这道题的时候就会感到懵逼。

时过一年,在考场懵逼的我,现在再拿起这道题写,脑子里有时也是一个大写的懵逼。

怎么办?找规律。

小人的“左”和“右”都是相对方向,相对于其脸朝外还是朝里。在考场上,我的想法是写四个if,然后模拟找的过程。

可惜当时因为调不出来,心里略有紧张,最后炸了。

今天,我的想法也是这样,但我发现了一个规律。

小人的初始朝向dir只有0和1,0朝里,1朝外,转向时候输入的dir,0表示相对向左,1表示相对向右。

这样应该是有四个组合:(0,0)、(0,1)、(1,0)和(1,1)。不过,它可以被简化成两个情况。

接下来的讨论如果方向感不强可能会懵逼。为了说明方便,我规定一个正方向:沿着输入的表向下的方向为正方向。

简化成的情况就只有两个方向,一个是我个人定义的正方向,另一个就是个人定义反方向(也就是沿着输入表向上数)。

不要被题目上所说的给出的逆时针顺序迷惑,这里不讨论顺逆时针的问题,只讨论我个人定义的方向。

也就是说,各位在看我这篇题解的时候,就不要想顺逆时针了,那样想多了会晕,亲测。

为了方便,我简称正方向和反方向。

这道题的规定,反方向数到表头的时候,从表尾继续;正方向数到表尾的时候,从表头继续。

这两句加粗的话将是下面讨论的基础。

对于组合(0,0),代表朝里向左数,换到自定义方向就是反方向。

对于组合(0,1),代表朝里向右数,换到自定义方向就是正方向。

对于组合(1,0),代表朝外向左数,换到自定义方向就是正方向。

对于组合(1,1),代表朝外向右数,换到自定义方向就是反方向。

不理解这四句话请和图片对照一下,正确性显然。

发现什么规律了吗?正方向的组合,两数之和为1,反方向的组合,两数之和为0或2。

那么我们开一个临时变量t,t代表小人初始方向参数值和当前转向参数值之和,根据上面的结论,只会有0,1,2三种结果。而其中,0和2代表的是同一种结果。

想到什么?对2取模!

对2取模得到的结果只有0和1,正好对应反方向和正方向。

那就好办了。如果是正方向,求出当前位置+偏移数量的值,再对n取模,就是下一个位置。

如果是反方向,那就求出当前位置-偏移数量的值,等等,这有可能会变成负的,那再加一个n就好。仍然是再对n取模得到位置。

最后得到一个位置,输出那个位置的名字就好。

附参考代码:

#include <iostream>
#include <string>
#define maxn 100005
using namespace std;
struct peo{
    int dir;
    string nm;
};
peo a[maxn];
int n,m;
int dir,s;
int now = 0;
int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (register int i=0;i<n;i++)
        cin >> a[i].dir >> a[i].nm;

    for (int i=1;i<=m;i++){
        cin >> dir >> s;
        int t = a[now].dir + dir;
        if (t % 2 == 0)
            now = (now - s + n) % n;
        else
            now = (now + s) % n;     
    }
    cout << a[now].nm << endl;
    return 0;
}


来源:ShawnZhou