题目链接
思路
不妨将这个字符串看成是一个26进制的数字
abc...k
26n−1a+X=h0
mod
p...(1)
26X+a=h1
mod
p...(2)
26∗(1)−(2)
得
26na−a=(26h0−h1)
mod
p
继续手推发现对于任意的
hi,hi+1
均满足
t=(26hi−hi+1)modp26n−1
,其中
t
是旋转了
只要用乘法逆元便可很容易在 O(n) 时间推出这个数字的每一位。
但是要注意到,做乘法逆元之前一定要注意分数的分母不为0,而此题尽管保证一定有解,但是没有保证
26n
mod
p=1
,因此要特判一下(比赛时几乎所有人没有注意到这一点,大部分人因此只拿到了50分),当
26n
mod
p=1
时,不能做乘法逆元,此时对于
hi
来说,
hi+1
的值只和
hi
有关,与旋转
i
次后个位的字母无关,此时只需要构造一个长度为
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 210000
using namespace std;
typedef long long int LL;
LL h[MAXN],pow[MAXN];
int n;
LL p;
LL extGCD(LL a,LL b,LL &x,LL &y) //ax+by=1
{
if(b==0)
{
x=1;
y=0;
return a;
}
LL tmp=extGCD(b,a%b,x,y);
LL t=x;
x=y;
y=t-(a/b)*y;
return tmp;
}
LL rev(LL a,LL b) //求模b意义下a的逆元x,ax=1(p b),b是质数所以可以求逆元(gcd(a,b)=1)
{
LL x,y;
extGCD(a,b,x,y);
x=(x%b+b)%b;
return x;
}
LL ans[MAXN];
int main()
{
scanf("%d%lld",&n,&p);
pow[0]=1;
for(int i=1;i<=2*n;i++)
pow[i]=(pow[i-1]*26)%p;
for(int i=1;i<=n;i++)
scanf("%lld",&h[i]);
if(pow[n]==1)
{
for(int i=1;i<=n;i++)
ans[n-i+1]=(h[1]%26),h[1]/=26;
for(int i=1;i<=n;i++)
printf("%c",(int)ans[i]+'a');
printf("\n");
return 0;
}
for(int i=1;i<n;i++)
ans[i]=((((h[i]*26)%p-h[i+1]+p)%p)*rev(pow[n]-1,p))%p;
ans[n]=((((h[n]*26)%p-h[1]+p)%p)*rev(pow[n]-1,p))%p;
for(int i=1;i<=n;i++)
printf("%c",((int)ans[i]+'a'));
printf("\n");
return 0;
}