【多项式转化+分治FFT+大整数处理】CC_LUCASTH Lucas Theorem

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/88430365

【题目】
Codechef
集合 [ n ] [n] 表示由 1 n 1\sim n 所有整数构成的集合,定义:
f ( n , k ) = S [ n ] S = k x S x f(n,k)=\sum_{S\subseteq [n]\\|S|=k} \prod_{x\in S}x
特别地, f ( n , 0 ) = 1 f(n,0)=1 ,求对于所有 k [ 0 , n ] k\in[0,n] ,有多少 f ( n , k ) f(n,k) 无法被一个素数 p p 整除。

n 1 0 501 , p 1 0 5 n\leq 10^{501},p\leq 10^5

【解题思路】
首先我们需要观察到我们要求的 f ( n , k ) f(n,k) 实际上是什么?他就是 ( x + 1 ) (x+1) n n 次上升幂的 x n k x^{n-k} 项的系数,即:
[ x n k ] i = 1 n ( x + i ) [x^{n-k}]\prod_{i=1}^n (x+i)
这个东西也很好理解,就是我们选择 k k 个数字相乘,如果选择了数字次数就不变,如果选择了 x x 次数就会 + 1 +1

现在我们可以在 O ( n log 2 n ) O(n\log ^2n) 的时间内通过分治 FFT \text{FFT} 简单得到答案了。

接下来我们需要进行更多推导,不妨设上面那个柿子为 P ( x ) P(x) ,我们要求的就是模 p p 意义下 P ( x ) P(x) 有多少项非零(接下来讨论都是在这个意义下)。令 c = n p , b = n  mod  p c=\lfloor \frac {n} p\rfloor,b=n\text{ mod }p ,那么我们有:
P ( x ) = ( i = 1 p ( x + i ) ) c ( i = 1 b ( x + i ) ) P(x)=(\prod_{i=1}^p(x+i))^c(\prod_{i=1}^b(x+i))
(就是前半部分稍微在模意义下进行了改动)

同时我们还有一个结论:
i = 1 p ( x + i ) ( x p x ) ( mod p ) \prod_{i=1}^p(x+i) \equiv (x^p-x) \quad(\text{mod p})
可以在 wiki \text{wiki} 上看到证明(也许是用费马小定理和亨泽尔引理),大概是左右两边多项式都有 p ? p? 个根。

由于我们只需要求有多少项非零,所以将 ( x p x ) (x^p-x) 替换为 ( x p 1 1 ) (x^{p-1}-1) 不会有影响,那么现在不妨做以下替换:
f ( x ) = ( x p 1 1 ) g ( x ) = i = 1 b ( x + i ) P ( x ) = f c ( x ) g ( x ) f(x)=(x^{p-1}-1)\\g(x)=\prod_{i=1}^b(x+i)\\P(x)=f^c(x) \cdot g(x)
特别地,若 n  mod  p = p 1 n\text{ mod }p=p-1 ,令 c = n p + 1 , g ( x ) = 1 c=\lfloor \frac n p \rfloor +1,g(x)=1 ,这个替换显然是成立的(多乘一项(x+p)对答案是没有影响的,只会将整体右移一位)

由于我们有 f c ( x ) = i = 0 c x i ( p 1 ) ( 1 ) k i ( c i ) f^c(x)=\sum\limits_{i=0}^c x^{i(p-1)}(-1)^{k-i}{c\choose i} ,即 f c ( x ) f^c(x) 只含 x i ( p 1 ) x^{i(p-1)} 项,且 deg ( g ) < p 1 \text{deg}(g)<p-1 ,于是我们可以分别考虑 f c ( x ) f^c(x) 的非零项数和 g ( x ) g(x) 的非零项数,再将两个乘起来即可。

对于 g ( x ) g(x) ,显然我们可以分治 FFT \text{FFT} 做。

对于 f c ( x ) f^c(x) ,我们只需要看 ( c i ) c\choose i 是否被 p p 整除,这个根据 Lucas \text{Lucas} 定理,如果将 c , i c,i 都写成 p p 进制数, ( c i ) 0 ( mod p ) c {c\choose i}\equiv 0 \quad (\text{mod p})\Leftrightarrow c 的每一位都 i \ge i ,那么设 c c 的每一位分别为 c i c_i ,答案就是 i ( c i + 1 ) \prod\limits_i (c_i+1)

于是总的时间复杂度是 O ( log 2 n + p log 2 p ) O(\log ^2 n+p\log ^2 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;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/88430365
今日推荐