【CF17D】Notepad【拓展欧拉定理】

题目大意:

题目链接:https://codeforces.com/problemset/problem/17/D
你有一个本子,你要往上面写全部的长度为nnaa进制数字,每一页可以写pp个。要求所有数字必须严格不含前导00。求最后一页上有多少个数字。


思路:

这道题的范围是2a101000,000,1n101000,000,1p1092leq aleq 10^{1000,000},1leq nleq 10^{1000,000},1leq pleq 10^9
一个aa进制的数字的每一位有aa种取值,但是题目要求不能有前导0,所以第一位只有a1a-1种取值。
所以满足要求的aa进制数有(a1)an1(a-1)a^{n-1}个。
所以题目要求我们求得就是
(a1)an1mod   p(a-1)a^{n-1}mod p
但是当p(a1)an1p|(a-1)a^{n-1}时应输出pp。因为此时最后一页有0个字,相当于这一页没写过,也就不是最后一页了。
a,na,n很大,但是p109pleq 10^9。根据扩展欧拉定理,有an1a(n1) mod φ(p)+φ(p)(mod p)a^{n-1}equiv a^{(n-1) mod varphi(p)+varphi(p)}(mod p)。所以我们可以把指数用拓展欧拉定理降到10910^9以内,然后用快速幂即可。
aa可以边读入边取模。反正ab mod p=(a mod p)×(b mod p) mod pab mod p=(a mod p) imes (b mod p) mod p
时间复杂度O(len)O(len)


代码:

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int N=1000010;
char sa[N],sn[N];
ll p,a,n,phi,q,ans;
int len1,len2;
bool flag;

ll power(ll x,ll k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%p)
		if (k&1) ans=ans*x%p;
	return ans;
}

int main()
{
	scanf("%s %s %lld",sa+1,sn+1,&p);
	phi=q=p; len1=strlen(sa+1); len2=strlen(sn+1);
	for (ll i=2;i*i<=q;i++)
		if (!(q%i))
		{
			phi=phi/i*(i-1);
			while (!(q%i)) q/=i;
		}
	if (q>1) phi=phi/q*(q-1);
	for (int i=1;i<=len1;i++)
		a=(a*10+sa[i]-48)%p;
	for (int i=len2;i>=1;i--)  //n-1
		if (sn[i]==48) sn[i]='9';
		else
		{
			sn[i]--;
			break;
		}
	for (int i=1;i<=len2;i++)
	{
		n=n*10+sn[i]-48;
 		if (n>=phi) flag=1;
		n%=phi;
	}
	if (flag) n+=phi;
	ans=((a-1)*power(a,n)%p+p)%p;  //注意这里可能为负数,所以要加p再模p,被HACK了一次
	if (ans) printf("%lld",ans);
		else printf("%lld",p);
	return 0;
}
原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998062.html