[UOJ 74] 【UR #6】破解密码

题目链接:UOJ - 74

 

题目分析

题目中,将字符串 S 的第一个字符移到末尾,其他字符向前移动一个位置,f(S) 就从 Hi 变成了 Hi+1。

我们分析一下这个过程:假设第一个字符为 c, (Hi - 26^(n-1) * c) * 26 + c = Hi+1

26 * Hi - Hi+1 = (26^n - 1) * c

c = (26 * Hi - Hi+1) * Inv(26^n - 1)

那么每一个 c 都可以直接解出来,由于题目保证有解,所以每一个 c 解出来的一定都是 [0, 25] 的数。

嗯..看起来非常对..提交上去....WA.50

为什么只得了50分呢?因为:“有除法的时候一定要考虑没有逆元的情况!

当 (26^n - 1) % p = 0 的时候,26^n - 1 是没有逆元的。而这样的点在数据里有 5 个 【QAQ】。

这样在前面的方程 26 * Hi - Hi+1 = (26^n - 1) * c 中含有 c 的项系数为 0,被消去,变成了 Hi * 26 = Hi+1 。

因为方程与 c 无关,所以 c 是任何一个字母这个方程都可以成立。

那么就要根据 f(S) = Hi 来求出这个 S 了,我们知道 f(S) 相当于是把字符串看做一个 26 进制数,然后这里的 Hi * 26 = Hi+1。

所以我们把 H0 看做26进制数求出对应的字符串,之后向左移一位就相当于将 H 乘了 26 ,依然是符合 f(S) = Hi+1 的。

 

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

const int MaxN = 100000 + 5;

typedef long long LL;

int n, p, t;
int H[MaxN]; 

LL A0, Temp;
LL X[MaxN];

LL Pow(LL a, int b) 
{
	LL ret = 1, f = a;
	while (b) 
	{
		if (b & 1) 
		{
			ret *= f;
			ret %= p;
		}
		b >>= 1;
		f *= f;
		f %= p;
	}
	return ret;
}

LL NY(LL x) 
{
	x = ((x % p) + p) % p;
	return Pow(x, p - 2);
}

int main()
{
	scanf("%d%d", &n, &p);
	for (int i = 0; i < n; ++i) scanf("%d", &H[i]);
	if (Pow(26ll, n) != 1) 
	{	
		Temp = NY(Pow(26ll, n) - 1);
		for (int i = 0; i < n; ++i) 
		{
			if (i == n - 1) t = 0;
			else t = i + 1; 
			X[i] = ((LL)H[i] * 26 - (LL)H[t]) % p * Temp;
			X[i] = ((X[i] % p) + p) % p;
		}
	}
	else
	{
		int Pos = n;
		while (H[0])
		{
			X[--Pos] = H[0] % 26;
			H[0] /= 26;
		}
	}
	for (int i = 0; i < n; ++i) printf("%c", 'a' + X[i]);
	printf("\n");
	return 0;
}

  

posted @ 2015-03-09 07:47  JoeFan  阅读(393)  评论(0编辑  收藏  举报