【题目】
Codechef
集合
表示由
所有整数构成的集合,定义:
特别地,
,求对于所有
,有多少
无法被一个素数
整除。
【解题思路】
首先我们需要观察到我们要求的
实际上是什么?他就是
的
次上升幂的
项的系数,即:
这个东西也很好理解,就是我们选择
个数字相乘,如果选择了数字次数就不变,如果选择了
次数就会
。
现在我们可以在 的时间内通过分治 简单得到答案了。
接下来我们需要进行更多推导,不妨设上面那个柿子为
,我们要求的就是模
意义下
有多少项非零(接下来讨论都是在这个意义下)。令
,那么我们有:
(就是前半部分稍微在模意义下进行了改动)
同时我们还有一个结论:
可以在
上看到证明(也许是用费马小定理和亨泽尔引理),大概是左右两边多项式都有
个根。
由于我们只需要求有多少项非零,所以将
替换为
不会有影响,那么现在不妨做以下替换:
特别地,若
,令
,这个替换显然是成立的(多乘一项(x+p)对答案是没有影响的,只会将整体右移一位)
由于我们有 ,即 只含 项,且 ,于是我们可以分别考虑 的非零项数和 的非零项数,再将两个乘起来即可。
对于 ,显然我们可以分治 做。
对于 ,我们只需要看 是否被 整除,这个根据 定理,如果将 都写成 进制数, 的每一位都 ,那么设 的每一位分别为 ,答案就是
于是总的时间复杂度是
WIKI证明相关
Finite field
Hensel’s lemma
Fermat’s little theorem
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
const db pi=acos(-1);
const int N=262333,mod=1e9+7;
namespace Poly
{
int m,L,rev[N];
struct cd
{
db r,i;
cd(db _r=0,db _i=0):r(_r),i(_i){}
cd operator + (const cd&rhs)const{return cd(r+rhs.r,i+rhs.i);}
cd operator - (const cd&rhs)const{return cd(r-rhs.r,i-rhs.i);}
cd operator * (const cd&rhs)const{return cd(r*rhs.r-i*rhs.i,r*rhs.i+i*rhs.r);}
cd operator / (const db&rhs)const{return cd(r/rhs,i/rhs);}
}A[N],B[N];
void fft(cd *a,int n,int f)
{
for(int i=0;i<n;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
{
cd wn(cos(pi/i),f*sin(pi/i));
for(int j=0;j<n;j+=(i<<1))
{
cd w(1,0);
for(int k=0;k<i;++k,w=w*wn)
{
cd x=a[j+k],y=w*a[i+j+k];
a[j+k]=x+y;a[i+j+k]=x-y;
}
}
}
if(!~f) for(int i=0;i<n;++i) a[i]=a[i]/n;
}
void mult(int *a,int *b,int *c,int n,int p)
{
for(L=0,m=1;m<=n;m<<=1,++L);
for(int i=0;i<m;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
for(int i=0;i<n;++i) A[i]=cd(a[i],0),B[i]=cd(b[i],0);
for(int i=n;i<m;++i) A[i]=B[i]=cd(0,0);
fft(A,m,1);fft(B,m,1);
for(int i=0;i<m;++i) A[i]=A[i]*B[i];
fft(A,m,-1);
for(int i=0;i<m;++i) c[i]=(ll)(A[i].r+0.5)%p;
}
}
namespace DreamLolita
{
int T,p,len,ans,n;
int a[N],bit[N],f[30][N];
char s[N];
int getmod()
{
int res=0;
for(int i=len;i;--i) res=(res*10+a[i])%p;
return res;
}
void divide()
{
int res=0;
for(int i=len;i;--i) res=res*10+a[i],a[i]=res/p,res%=p;
for(;len && !a[len];--len);
}
void solve(int l,int r,int dep)
{
int len=r-l+1;
for(int i=0;i<=len<<1;++i) f[dep][i]=0;
if(l==r) {f[dep][0]=1;f[dep][1]=l;return;}
int mid=(l+r)>>1;
solve(l,mid,dep);solve(mid+1,r,dep+1);
Poly::mult(f[dep],f[dep+1],f[dep],(r-l+1),p);//wrong because (r-l+2),cross the limits
}
void solution()
{
scanf("%d",&T);
while(T--)
{
scanf("%s%d",s+1,&p);len=strlen(s+1);
reverse(s+1,s+len+1);
for(int i=1;i<=len;++i) a[i]=s[i]-'0';
for(n=0;len;){bit[++n]=getmod();divide();}
bit[n+1]=0;
if(bit[1]==p-1)
{
bit[1]=0;
for(int i=2;;++i)
{
n=max(n,i);
if(++bit[i]==p) bit[i]=0;
else break;
}
}
solve(0,bit[1],0);ans=0;
for(int i=0;i<=bit[1];++i) if(f[0][i]) ++ans;
for(int i=2;i<=n;++i) ans=1ll*ans*(bit[i]+1)%mod;
printf("%d\n",ans);
}
}
}
int main()
{
#ifdef Durant_Lee
freopen("CC_LUCASTH.in","r",stdin);
freopen("CC_LUCASTH.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}