11368: 【原1368】丁姐的猴子
题目
题目描述
author: SSY 原OJ链接:https://acm.sjtu.edu.cn/OnlineJudge-old/problem/1368
Description
丁姐和二哥一样养了很多猴子,每只猴子脖子上都挂着一个数字号码牌(数字可能重复但是不重要),一天他和这些猴子完游戏。一开始时有n只猴子围成一圈,猴子的号码牌按顺序为1~n。从当前猴子开始1~k报数,报到k后可能由两种操作:
①报到k的猴子从圈中出去,由刚刚退出的猴子的下一只猴子再开始报数。
②在报到k的猴子后边加一只号码牌为w的猴子,由刚刚加入的猴子开始再报数。
初始时从编号为1的猴子开始报数,求最后圈内所有猴子的号码牌上数字之和。
Input Format
输入第一行两个数,分别为初始时的猴子数量n和操作数量m。
接下来m行,每行描述一个操作:
0 k,删除报到k的猴子。
1 k w,在报到k的猴子后面加入一只号码牌为w的猴子。
保证圈内至少有一只猴子。
Output Format
输出一行,一个数,表示圈内剩下猴子号码牌之和。
Sample Input
5 5
0 2
0 4
1 1 10
0 1
1 2 3
Sample Output
15
说明
对于30%的数据,n<1000,m<1000
对于100%的数据,n<10000, m<10000,w<100000
对于每次操作链表中的元素为(括号里的是下一次报1的猴子):
5 5 //[(1),2,3,4,5]
0 2 //[1,(3),4,5]
0 4 //[(3),4,5]
1 1 10 //[3,(10),4,5]
0 1//[3,(4),5]
1 2 3//[3,4,5,(3)]
yyong119's solution
#include <iostream>
#include <cstdio>
using namespace std;
typedef unsigned long long ULL;
int number[100000 + 10]; //下标是流水id 内容是身上的编号
int front[100000 + 10];//流水id的操作
int nex[100000 + 10];//流水id的操作
//模拟法
void out(int x) {
nex[front[x]] = nex[x];
front[nex[x]] = front[x];
}
void in(int x, int w) { //此处的W指的是流水id
nex[w] = nex[x];
nex[x] = w;
front[nex[w]] = w;
front[w] = x;
}
int main(int argc, char const *argv[])
{
int M;//猴子的数量M
int m; //操作的数量m
cin >> M >> m;
for (int i = 1; i <= M; ++i)
{
number[i] = i;//第i个猴子身上的编号是i
front[i] = (i == 1) ? M : i - 1;
nex[i] = (i == M) ? 1 : i + 1;
}
int cur = 1;//接下来要报1的猴子编号
int cnt = M;//还有多少只猴子
int K;//数到K要进行操作
ULL res = ((1 + M)*M) / 2;
for (int i = 0; i < m; ++i)//m次操作
{
int flag;
scanf("%d", &flag);
if (flag == 0) {//要开始删除猴子
scanf("%d", &K);
K %= (cnt);
if (K == 0) K += cnt;
for (int i = 0; i < K - 1; ++i)
cur = nex[cur];//报数的过程
out(cur);
res -= number[cur];//减少剩余猴子所组成的编号之和
cur = nex[cur];
cnt--;//出去一只猴子
}
else {
//不删除 只加入
int W;
scanf("%d %d", &K, &W);
K %= (cnt);
number[++M] = W;//流水ID
if (K == 0) K += cnt;
for (int i = 0; i < K - 1; ++i)
cur = nex[cur];//报数的过程
in(cur, M);//cur的后面加入一个W
res += W;
cur = M;
cnt++;//加入一只猴子
}
}
cout << res << endl;
return 0;
}