题目链接:bzoj2219
题目大意:
对于给定的3个非负整数 A,B,K,求
xA=B(mod2K+1)
在[0,2K]范围内
x
的个数。
题解:
rt..数论
大概涉及到中国剩余定理的推论,BSGS,原根,指标等知识。
这道题跟bzoj1319&bzoj1420最大的不同就是模数是个奇数(即不一定是质数)
那么我们考虑把它分解质因数:
2K+1=pt11pt22⋅⋅⋅ptrr
因为有个中国剩余定理的推论(根本不知道为什么):
一个取模数互质的同余方程组(未必线性),组合之后,这个同余方程组的解的个数为各方程解的个数的乘积。
[大概意思就是从每个方程的解中选一个组合在一起,那么就有各方程解的个数的乘积种。但是它怎么能保证这样得到每个解都是互不相等的呢?求dalao告知]
所以我们只用处理 xA=B(modptii) 这样的方程的解的个数,相乘就是答案了。
以下直接写作
p
和
因为可能出现B模上
pt
为0这种情况,所以讨论一下:
Bmodpt=0
时:
那么原方程就变为:
于是就要求 xA 是 pt 的倍数。
设 xA=(pk)A=pkA ,那么即要求 kA≥t
于是最小的 k=⌊t−1A⌋+1
由于解集为[0, pt )范围内的k的倍数,所以这个方程的解的数量就是 pt−k ,即 pt−(⌊t−1A⌋+1) 。
Bmodpt≠0
时:
那么我们把B写成
B′×pk
的形式
于是原方程就变为:
显然,如果A不能整除k,那么这个方程是无解的。
然后我们把方程转化为:
于是就跟 bzoj1319&bzoj1420一样了。
弄成指标得到一个形如 ax=b(modp) 的方程,我们又知道,求解模线性方程的话,当且仅当 gcd(a,p)∣b 时,解的个数为 gcd(a,n) 。于是我们判断无解的情况(答案为0),然后就可以根据这个得到该方程的解的个数了。
虽然得到答案了..但是会感觉有那么点奇怪。
因为 解的取值范围改变了!
原方程中 x 的取值范围是
注意此时不要把p-1当成 φ (模数)了啊,求原根求逆元的时候要注意啊。
参考题解:
http://blog.csdn.net/regina8023/article/details/44863519
http://blog.csdn.net/popoqqq/article/details/41595187
http://www.cnblogs.com/KonjakJuruo/p/5853816.html
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
#define maxn 51000
struct node
{
LL x,id;
bool operator < (const node y) const {return x<y.x;}
}hh[maxn];
LL qpow(LL x,LL t) {LL ret=1;while (t) {if (t&1) ret*=x;x*=x;t>>=1;}return ret;}
LL qpow(LL x,LL t,LL p)
{
LL ret=1;
while (t)
{
if (t&1) ret=(ret*x)%p;
x=(x*x)%p; t>>=1;
}return ret;
}
LL gcd(LL a,LL b){return (b==0)?a:gcd(b,a%b);}
LL twocut(LL r,LL k)
{
LL l=0;
while (l<=r)
{
LL mid=(l+r)>>1;
if (hh[mid].x==k) return hh[mid].id;
if (hh[mid].x<k) l=mid+1;
else r=mid-1;
}return -1;
}
LL BSGS(LL a,LL b,LL p,LL phi)//a^x=b (mod p)
{
LL m=ceil(sqrt(p));
LL ret=0,i,aj=1;
for (i=0;i<m;i++)
{
hh[i].x=aj;
hh[i].id=i;
aj=(aj*a)%p;
}sort(hh,hh+m);
LL cnt=0;hh[cnt]=hh[0];
for (i=1;i<m;i++) if (hh[i].x==hh[i-1].x) hh[++cnt]=hh[i];
LL ai=b,am=qpow(aj,phi-1,p);
for (i=0;i<m;i++)
{
LL w=twocut(cnt,ai);
if (w!=-1) {ret=w+m*i;break;}
ai=(ai*am)%p;
}
return ret;
}
LL cnt,pri[maxn];
void get_pri(LL p)
{
cnt=0;
for (LL i=2;i*i<=p;i++)
if (p%i==0)
{
pri[++cnt]=i;
while (p%i==0) p/=i;
}
if (p>1) pri[++cnt]=p;
}
LL get_r(LL p)//求原根
{
get_pri(p-1);LL g=2;
while (g<p)
{
bool bk=false;
for (LL i=1;i<=cnt;i++)
if (qpow(g,(p-1)/pri[i],p)==1) {bk=true;break;}
if (!bk) break; g++;
}return (g<p)?g:0;
}
LL solve(LL A,LL B,LL p,LL t)//x^A=B(mod p^t)
{
LL pt=qpow(p,t);
if (B%pt==0) return qpow(p,t-((t-1)/A+1));
LL k=0,ret;
while (B%p==0) k++,B/=p;
if (k%A!=0) return 0;
ret=qpow(p,k-k/A);t-=k;
LL g=get_r(p);
if (k!=0) pt=qpow(p,t);
LL phi=pt-pt/p;//对于phi(p^k)有phi(p^k)=(p-1)p^(k-1)=p^k-p^(k-1)
LL Ib,d=gcd(A,phi);
Ib=BSGS(g,B,pt,phi);
if (Ib%d!=0) return 0;
ret*=d;return ret;
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int T;LL A,B,K,i,ans,t;
scanf("%d",&T);
while (T--)
{
scanf("%lld%lld%lld",&A,&B,&K);//x^A=B(mod 2K+1)
ans=1;K=2*K+1;
for (i=2;i*i<=K;i++)
if (K%i==0)
{
t=0;
while (K%i==0) t++,K/=i;
ans*=solve(A,B,i,t);
if (ans==0) break;
}
if (K!=1) ans*=solve(A,B,K,1);
printf("%lld\n",ans);
}
return 0;
}