bzoj-2219 數論之神
題意:
求方程X^A = B(mod 2*K + 1)
X ∈[0, 2K] 內的解的個數;
題解:
一道數論的好題。
涉及知識點大概有:Crt推論。BSGS,EXGCD,原根與指標;
這道題的主要問題在於兩點:
第一點:取模數不是質數,無法利用通常的方式解方程。
可是有中國剩余定理這個東西,定理的推論告訴我們:
一個取模數互質的同余方程組(未必線性),組合起來之後。這個同余方程解的個數為各方程解的個數的乘積;
(組合起來的方程的取模數為全部數的積;實際上這裏解的範圍都是屬於[0 ,自己取模數) )
這點十分重要呢,它不僅證明了解的求法。並且假設有隨意一個方程無解,那麽整個就都是無解的。
攻克了這一點之後,就是第二點:怎樣處理一個方程的解的個數。
暫且令當前方程為x^A =B (mod p^d);
由於中國剩余定理要求取模數互質。所以將2*K+1分解質因子作為全部方程;
然而我僅僅會處理p^1的情況怎麽辦啊= =;
分類討論:
p^d|B :這時p^d是B的因子,那麽實際上就是x^A =0 (mod p^d)了;
假設設x=p^k*q。這裏(p,q)=1;
k事實上表示的是x中p因子的個數,那麽在x^A中的個數就是k*A,也就有k*A>=d;
k>=d/A,也就是說k最小為ceil(d/A) (ceil為上取整函數);
由於x的取值範圍是[0,p^d),而x=p^k*q ,顯然d>k。
所以解的個數就是p^d/p^k=p^(d-k)。
gcd(p^d,B)=p^k:這時二者之間有一個公約數;
直觀的想。假設等式兩邊同一時候除一個p^k就解決啦;
可是要保證A|k,要不然的話x^A中約數p的個數就不可能和B的相等(顯然);
當我們成功的把方程p^k*x‘^A= B (mod p^d)
化簡為 x‘^A= B/(p^k) (mod p^(d-k))之後
我們就掛啦!
由於這樣是不正確的,確切的說是x‘的取值範圍變化了;
那麽變化了多少呢?原來的範圍是[0,p^(d-k/A))。而後變成了[0,p^(d-k))。
原因就在於mod值的縮小,可是對於答案的影響和上面一樣:
答案是第二個方程答案的p^(k-k/A)倍。
第二個方程怎麽解? 取指標啊!
取完指標就是線性同余方程了。方程個數就是gcd(A,φ(p^d));
可是有個坑,同余方程可能無解啊。
。
所以還要大步~小步~BSGS求出一個 以p^d的原根為底,關於B的。對p^d取模的 指標lnB (好TM繞);
然後推斷gcd(A,φ(p^d))| lnB,假設不是因子則無解!
那麽這題就結束了,可喜可賀;
什麽gcd(B,p^d)=1沒討論?事實上不就是上一段怎麽解的事嗎。
什麽復雜度?我但是BZ這題Rank1 (倒數)的人啊;
【反正我咋看咋O(n);
反正我是線性篩搞了個素數表= =
然並卵。似乎復雜度沒有本質改變?
代碼:
#include<math.h> #include<stdio.h> #include<string.h> #include<algorithm> #define N 140142 using namespace std; typedef long long ll; const ll INF=0x7f7f7f7f7f7f7f7f7fll; struct Hash_Set { ll head[N],next[N],X[N],val[N],tot; void clear() { tot=0; memset(head,0,sizeof(head)); memset(next,0,sizeof(next)); memset(val,-1,sizeof(val)); memset(X,0,sizeof(X)); } ll& operator [](ll x) { ll index=x%N; for(ll i=head[index];i;i=next[i]) { if(X[i]==x) return val[i]; } next[++tot]=head[index]; head[index]=tot; X[tot]=x; return val[tot]; } }hash; ll pri[N],top,a[N],b[N]; bool vis[N]; void init() { for(ll i=2;i<N;i++) { if(!vis[i]) pri[++top]=i; for(ll j=1;j<=top&&i*pri[j]<N;j++) { vis[i*pri[j]]=1; if(i%pri[j]==0) break; } } } ll pow(ll x,ll y,ll mod) { ll ret=1; while(y) { if(y&1) ret=ret*x%mod; x=x*x%mod; y>>=1; } return ret; } ll gcd(ll a,ll b) { ll t=a%b; while(t) { a=b,b=t; t=a%b; } return b; } void exgcd(ll a,ll b,ll &x,ll &y,ll &d) { if(!b) x=1,y=0,d=a; else { exgcd(b,a%b,y,x,d); y-=a/b*x; } } ll inv(ll X,ll mod) { ll x,y,d; exgcd(X,mod,x,y,d); return (x%mod+mod)%mod; } ll Root(ll x,ll phi) { ll st[N],top=0,temp=phi; for(ll i=1,p=2;p*p<=temp;i++,p=pri[i]) { if(temp%p==0) { st[++top]=phi/p; while(temp%p==0) temp/=p; } } if(temp!=1) st[++top]=phi/temp; for(ll i=1,j;i<=phi;i++) { for(j=1;j<=top;j++) { if(pow(i,st[j],x)==1) break; } if(j>top) return i; } } ll BSGS(ll A,ll B,ll C) { hash.clear(); ll bk=ceil(sqrt(C*1.0)),i,k,D,temp; for(i=0,D=1;i<bk;i++,D=D*A%C) { if(hash[D]==-1) hash[D]=i; } temp=inv(D,C); for(i=0,k=B;i<=bk;i++,k=k*temp%C) { if(hash[k]!=-1) return i*bk+hash[k]; } return 0; } ll slove(ll A,ll B,ll C) { ll i,j,k,p,mod,lnB,g,d,cnt,ans; for(i=1,p=2,ans=1;p*p<=C;i++,p=pri[i]) { if(C%p==0) { mod=1,cnt=0; while(C%p==0) C/=p,mod*=p,cnt++; if(B%mod==0) { d=pow(p,cnt-ceil(cnt*1.0/A),INF); } else { k=0; while(B%p==0) k++,B/=p; if(k%A) d=0; else { g=Root(mod,mod-mod/p); lnB=BSGS(g,B,mod); d=gcd(A,mod-mod/p); if(lnB%d) d=0; d*=pow(p,k-k/A,INF); } } ans*=d; } } if(C!=1) { mod=C,p=C,cnt=1; if(B%mod==0) { d=pow(p,cnt-ceil(cnt*1.0/A),INF); } else { k=0; while(B%p==0) k++,B/=p; if(k%A) d=0; else { g=Root(mod,mod-mod/p); lnB=BSGS(g,B,mod); d=gcd(A,mod-mod/p); if(lnB%d) d=0; d*=pow(p,k-k/A,INF); } } ans*=d; } return ans; } int main() { init(); ll c,T,A,B,C; scanf("%lld",&T); for(c=1;c<=T;c++) { scanf("%lld%lld%lld",&A,&B,&C); printf("%lld\n",slove(A,B,C<<1|1)); } return 0; }
bzoj-2219 數論之神