【BZOJ 1409】 Password 数论(扩展欧拉+矩阵快速幂+快速幂)
读了一下题就会很愉快的发现,这个数列是关于p的幂次的斐波那契数列,很愉快,然后就很愉快的发现可以矩阵快速幂一波,然后再一看数据范围就......然后由于上帝与集合对我的正确启示,我就发现这个东西可以用欧拉函数降一下幂,因为两个数一定互质因此不用再加一个phi(m),于是放心的乘吧宝贝!!
#include <cstdlib> #include <cstring> #include <cstdio> #include <iostream> #include <cmath> #define r register using namespace std; typedef long long LL; inline LL read() { r LL sum=0; r char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9') { sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar(); } return sum; } LL prime[600000]; bool isnot[600000]; LL T; LL temp_a[4][4],a[4][4],b[4],temp_b[4]; LL m,p; inline void Init() { for(r LL i=2;i<=(1<<16);i++) { if(!isnot[i]) prime[++T]=i; for(r LL j=1;j<=T&&prime[j]*i<=(1<<17);j++) { isnot[prime[j]*i]=1; if(i%prime[j]==0)break; } } m=read(),p=read(); } inline LL Opha(LL x) { r LL to=(LL)sqrt(x+0.5); r LL ans=x; for(r LL i=1;prime[i]<=to;i++) if(x%prime[i]==0) { ans=ans/prime[i]*(prime[i]-1); while(x%prime[i]==0)x/=prime[i]; } if(x!=1) ans=ans/x*(x-1); return ans; } inline void Multi_One(LL k) { memset(temp_b,0,sizeof(temp_b)); for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) temp_b[i]=(temp_b[i]+a[i][j]*b[j]%k)%k; memcpy(b,temp_b,sizeof(b)); } inline void Multi_Two(LL K) { memset(temp_a,0,sizeof(temp_a)); for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) for(int k=1;k<=2;k++) temp_a[i][j]=(temp_a[i][j]+a[i][k]*a[k][j]%K)%K; memcpy(a,temp_a,sizeof(a)); } inline LL POW(LL x,LL k) { a[1][1]=1%k,a[1][2]=1%k,a[2][1]=1%k,a[2][2]=0; b[1]=1%k,b[2]=0; while(x) { if(x&1)Multi_One(k); x>>=1,Multi_Two(k); } b[1]%=k; return b[1]; } inline LL Pow(LL x,LL y,LL k) { LL ans=1%k; while(y) { if(y&1)ans=ans*x%k; y>>=1,x=x*x%k; } ans%=k; return ans; } inline void Work() { while(m--) { r LL n=read(),q=read(); r LL x=Opha(q); r LL y=POW(n-1,x); printf("%lld\n",Pow(p,y,q)); } } int main() { Init(); Work(); return 0; }
苟利国家生死以, 岂因祸福避趋之。