題目描述:
題目分析:
逆元可以看做一個僞隨機序列,前個數的逆元的最小值期望可以縮小到級別。
所以當時可以枚舉前個數,求出最小逆元,再枚舉逆元求出對應的位置,時間複雜度期望。
具體實現時也可以從小到大枚舉,記錄逆元的最小值,如果則計入答案,當時就可以退出了(將此時的所有答案翻轉過來也是答案,它是對稱的,比如答案有,那麼必然有)
由於多組數據,當過大的時候直接枚舉會超時,觀察能夠形成前綴最小值的,因爲當達到時期望已經了,所以不會很大。
我們可以枚舉,然後將用Pollard_Rho算法分解因數求出所有的,最後將其按排序,檢驗是否是前綴最小,輸出即可。時間複雜度爲
具體實現時取即可通過(我最慢的一個點跑了994ms)。
也有不設具體的值的方法,假設已經枚舉到了,已經分解出的因數爲,如果,說明在到之間不可能還有解,如果對於所有的這個不等式都成立(以及和),那麼說明當前枚舉到的已經足夠了。直接設k=40多好啊
Code:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int T;
namespace solve1{
const int N = 3000005;
int inv[N],q[N],cnt;
void main(LL p){
cnt=0,inv[0]=inv[1]=1;int mn=p;
for(int i=2;;i++){
inv[i]=(p-p/i)*inv[p%i]%p;
if(inv[i]<i) break;
if(inv[i]<mn) mn=inv[i],q[++cnt]=i;
}
printf("%d\n",cnt?cnt*2-(q[cnt]==inv[q[cnt]]):0);
for(int i=1;i<=cnt;i++) printf("%d %d\n",q[i],inv[q[i]]);
for(int i=cnt-(q[cnt]==inv[q[cnt]]);i>=1;i--) printf("%d %d\n",inv[q[i]],q[i]);
}
}
namespace solve2{
LL pr[65],base[6]={2,3,7,61,19260817};int pc;
inline LL mul(LL a,LL b,LL p){LL r=a*b-(LL)((long double)a/p*b+0.5)*p;return r<0?r+p:r;}
inline LL Pow(LL a,LL b,LL p){
LL s=1; for(;b;b>>=1,a=mul(a,a,p)) if(b&1) s=mul(s,a,p);
return s;
}
bool Miller_Rabin(LL n){
for(int i=0;i<5;i++) if(n==base[i]) return 1; else if(n%base[i]==0) return 0;
LL d=n-1;int k=0;
while(!(d&1)) d>>=1,k++;
for(int i=0;i<5;i++){
LL x=Pow(base[i],d,n),y;
for(int i=1;i<=k&&x!=1;i++,x=y)
if((y=mul(x,x,n))==1&&x!=n-1) return 0;
if(x!=1) return 0;
}
return 1;
}
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
LL Rho(LL n,int c){
LL x=rand()%n+1,y=x,d=1,q=1;
for(int k=1;;k<<=1,y=x,q=1){
for(int i=1;i<=k&&d==1;i++){
q=mul(abs((x=(mul(x,x,n)+c)%n)-y),q,n);
if(!(i&127)) d=gcd(q,n);
}
if(d>1||(d=gcd(q,n))>1) return d==n?Rho(n,c+1):d;
}
}
void Pollard(LL n){
while(!(n&1)) n>>=1,pr[++pc]=2;
if(n==1) return;
if(Miller_Rabin(n)) {pr[++pc]=n;return;}
LL p=Rho(n,3);
Pollard(p),Pollard(n/p);
}
pair<LL,LL>ans[3000005]; int tot,cnt;
LL d[60005];int dc;
void main(LL p){
tot=cnt=0;
for(int k=1;k<=40;k++){
LL t=k*p+1,pw;
pc=0,Pollard(t),sort(pr+1,pr+pc+1);
d[dc=1]=1,pr[pc+1]=0;
for(int i=1,j,e;i<=pc;i=j){
for(j=i+1,e=1;pr[i]==pr[j];j++,e++);
for(int j=dc,k;j>=1;j--)
for(k=1,pw=pr[i];k<=e;k++,pw*=pr[i]) d[++dc]=d[j]*pw;
}
for(int i=2;i<=dc;i++) if(d[i]<p&&t/d[i]<p) ans[++tot]=(make_pair(d[i],t/d[i]));
}
sort(ans+1,ans+1+tot); LL mn=p;
for(int i=1;i<=tot;i++)
if(ans[i].second<mn) mn=ans[i].second,ans[++cnt]=ans[i];
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++) printf("%lld %lld\n",ans[i].first,ans[i].second);
}
}
int main()
{
scanf("%d",&T);LL p;
while(T--){
scanf("%lld",&p);
if(p<=1e9) solve1::main(p);
else solve2::main(p);
}
}