循環節

循環節的定義

如果無限小數的小數點後,從某一位起向右進行到某一位止的一節數字循環出現,首尾銜接,稱這種小數爲循環小數,這一節數字稱爲循環節。

研究背景

給定一個整數nn,求最小的kk使10k110^k≡1 (modmod nn)。此時的kk就等價於1n\frac{1}{n}的循環節的長度。
引理:對於ak1a^k≡1 (modmod nn),若aann互質,那麼最小的kk必定是ϕ(n)\phi(n)的約數;若aann有公因子,顯然無解。
根據這個性質,只要枚舉ϕ(n)\phi(n)的每個約數分別驗證,就能求出循環節的最小長度kk了。

例題:Big Integer

鏈接

2019牛客暑期多校訓練營(第三場)D題

題意

給定n,m,pn,m,pA(a,b)A(a,b)表示一個由aba^b11組成的數。
求有多少對(i,j)(i,j),滿足1in1≤i≤n1jm1≤j≤m,使得A(i,j)0(modA(i,j)≡0(mod p)p)
(p,n,m109)(p,n,m≤10^9)

分析

11…111 = 10n19\frac{10^n-1}{9} ≡ 0 (mod p)
等價於10n110^n≡1 (mod(mod 9p)9p)
n2,5n≠2,5的倍數的時候,有gcd(10,9p)=1gcd(10,9p)=1,因此10ϕ(9p)110^{\phi(9p)}≡1 (mod(mod 9p)9p)
我們需要找到滿足10i10^i modmod 9p9p的最小循環節長度dd
根據前面說到的性質,dd必定是ϕ(9p)\phi(9p)的約數,
暴力枚舉ϕ(9p)\phi(9p)的約數分別驗證就能找到最小循環節dd
由於dd的倍數都可能作爲循環節,所以接下來我們要找到有多少對(i,j)(i,j),滿足dd | iji^j
dd進行質因子分解,得到d=p1k1p2k2...pxkxd=p_1^{k1}p_2^{k2}...p_x^{kx}
考慮當jj固定時,ii必須是g=p1k1jp2k2j...pxkxjg=p_1^{⌈\frac{k1}{j}⌉}p_2^{⌈\frac{k2}{j}⌉}...p_x^{⌈\frac{kx}{j}⌉}的倍數。
因此一共有ng\frac{n}{g}個合法的ii
由於所有ki30k_i≤30,所以當j30j≥30時,kij=1⌈\frac{ki}{j}⌉=1
因此只需計算從113030jjgg值即可。
p=2,5p=2,5的倍數時,顯然答案爲00

代碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
	x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	x*=f;
}
const int M=1e5+5;
int cas,cnt[M],mark[M];
ll n,m,p,prime[M];
ll Fast(ll a,ll b,ll P){
	ll res=0;
	while(b){
		if(b&1)res=(res+a)%P;
		a=(a+a)%P;
		b>>=1;
	}
	return res;
}
ll fast(ll a,ll b,ll P){
	ll res=1;
	while(b){
		if(b&1)res=Fast(res,a,P);
		a=Fast(a,a,P);
		b>>=1; 
	}
	return res;
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("jiedai.in","r",stdin);
//  freopen("jiedai.out","w",stdout);
#endif
	for(ll i=2;i<M;i++)if(!mark[i])
		for(ll j=i*i;j<M;j+=i)mark[j]=1;
	rd(cas);
	while(cas--){
		rd(p),rd(n),rd(m);
		if(p%2==0||p%5==0){puts("0");continue;}
		ll P=9*p,phi=1,tmp=P;
		for(ll i=2;i*i<=P;i++){
			if(!mark[i]){
				int tp=1;
				while(tmp%i==0){
					tmp/=i;
					if(tp)phi*=i-1,tp=0;
					else phi*=i;
				}
			}
		}
		if(tmp>1)phi*=tmp-1;
		ll d=phi,res=0,ans=0;
		for(ll i=1;i*i<=phi;i++)
			if(phi%i==0&&fast(10,i,P)==1)
				{MIN(d,i);break;}
		for(ll i=sqrt(phi);i;i--)
			if(phi%i==0&&fast(10,phi/i,P)==1)
				{MIN(d,phi/i);break;}
		tmp=d;
		for(ll i=2;i*i<=d;i++)if(!mark[i]&&d%i==0){
			prime[++res]=i;
			cnt[res]=0;
			while(tmp%i==0)cnt[res]++,tmp/=i;
		}
		if(tmp>1)prime[++res]=tmp,cnt[res]=1;
		for(int j=1;j<=min(30ll,m);j++){
			tmp=1;
			for(int i=1;i<=res;i++)
				for(int t=1;t<=(cnt[i]+j-1)/j;t++)
					tmp*=prime[i];
			ans+=n/tmp;
			if(j==30)ans+=(n/tmp)*(m-30);
		}
		printf("%lld\n",ans);
	}
	return (0-0);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章