「BZOJ1234 Pku3379」Alphabet

简单看过去,发现顺着做十分困难,不妨倒着做删除,发现如果删除则是一个约瑟夫环问题,性质特别得多。

于是我们考虑倒着删除还原。

先考虑一个整体上的思路,我们钦定最后得到的环的一号位是我们最后一个加入的位置也就是答案要求求的。

我们发现对于一号位,当我们删除了一号位的元素后,从顺向看新变成一号位的那个元素实际上产生了旧一号位,也就是说,与答案有关的位置永远都在一号位

从整体思路上看,我们最后不断删除是可以找到原字符环的实际一号位的位置与我们虚拟的一号位的位置的关系,从而找到虚拟一号位的实际位置,借助过程中一号位被删除的次数,我们就可以找到答案。

现在问题就是该如何模拟这样一个删除的过程。

不妨假设 \(x\) 为我们下一步要删除的位置编号。

考虑这样一个模拟方案,我们每次将当前环完成该删的删掉,同时找到在下一个环找到下个该删的位置。这样做有一个好处,就是我们直接算出来的编号就是我们的实际编号,而不需要额外进行变换。

但是大概需要考虑一种特殊情况是不能用上面方法进行计算的:

\(k > m\) 其中 \(m\) 表示当前环长,这样很有可能出现找到下一个要删的数要报很多圈数的情况,这种情况下报第一圈数要报 \(m\) 次,但接下来的圈每圈要报 \(m - 1\) 次,特判一下就好了。

其实也没什么细节,就是有点绕来绕去的。

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define pii pair <int , int>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
typedef long long LL;

template <typename T>
void read(T &x) {
	T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
	x *= f;
}

template <typename T>
void write(T x , char s='\n') {
	if(!x) {putchar('0');putchar(s);return;}
	if(x<0) {putchar('-');x=-x;}
	T tmp[25] = {} , t = 0;
	while(x) tmp[t ++] = x % 10 , x /= 10;
	while(t -- > 0) putchar(tmp[t] + '0');
	putchar(s);
}

const int MAXN = 1e4 + 5;

LL n , k , m;
char s[MAXN];

int main() {
	read(n),read(k),read(m);
	scanf("%s" , s + 1);
	m += n;
	LL x = 1 , tot = 0;
	while(m > n) {
		if(x == 1) tot ++;
		LL s = min(m - n , (m - x) / (k + 1) + 1);
		x += s * (k + 1);
		if(x > m) {
			x = (x - m) % (m - s);
			if(!x) x = m - s;
		}
		else {
			x -= s;
			break;
		}
		m -= s;
	} 
	putchar((s[x] - 'A' + tot) % 26 + 'A');
	return 0;
}
posted @ 2021-05-25 09:15  Reanap  阅读(45)  评论(0编辑  收藏  举报