链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3992
题解
不好做不好做,真心不好做。
这个题显然是dp,一开始我们这样设计状态
f[i][j]
表示长度为i的序列,积在模
M
意义下等于
那显然直接做的复杂度是
O(NM2)
,到了这里,有一种矩阵快速幂做法,可以直接优化到
O(NM3)
,应该能过30%。
显然正解不允许带有
O(M3)
,所以矩阵肯定是不行了。
到这里可能就需要原根了,求出原根,每个数都能表示成原根的幂,这样就可以用指数代替数,
f[i][j]
表示长度为
i
,乘积在模
这样转移就成了
很蛋疼的地方在于取模,不取模还好,取模怎么做。其实也没啥,每次求完卷积之后肯定会多出来一些不合法的,即它们的下标大于
M−1
,这个时候只需要把的值加到合法的位置就好了,意思就是
f[i modM−1]← f[i]
。
显然多项式卷积满足交换律,所以只需要把用来转移的那个多项式进行快速幂。
总的时间复杂度
O(MlogMlogN)
代码
//DP+NTT
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 131072
#define mod 1004535809ll
#define ll long long
using namespace std;
ll up, N, M, S[maxn], R[maxn], trans[maxn], f[maxn], inv, tmp[maxn], ans[maxn],
tmp1[maxn], tmp2[maxn], X, pr;
inline ll pow(ll a, ll b, ll p)
{
ll t=a, ans=1;
for(;b;b>>=1,t=t*t%p)if(b&1)ans=ans*t%p;
return ans;
}
void ntt(ll *a, ll n, ll opt)
{
ll i, j, k, p, w, wn, x, y;
for(i=0;i<n;i++)if(i<R[i])swap(a[i],a[R[i]]);
for(i=1;i<n;i<<=1)
{
wn=pow(3,(mod-1)/(i<<1),mod);
for(p=i<<1,j=0;j<n;j+=p)
for(w=1,k=0;k<i;k++,w=w*wn%mod)
{
x=a[k+j], y=a[k+j+i]*w;
a[k+j]=(x+y)%mod, a[k+j+i]=(x-y+mod)%mod;
}
}
if(opt==-1){reverse(a+1,a+n);for(i=0;i<n;i++)a[i]=(a[i]*inv)%mod;}
}
inline void juan(ll *a, ll *b, ll *c)
{
ll i;
for(i=0;i<up;i++)tmp1[i]=a[i], tmp2[i]=b[i];
ntt(tmp1,up,1); ntt(tmp2,up,1);
for(i=0;i<up;i++)c[i]=tmp1[i]*tmp2[i];
ntt(c,up,-1);
for(i=0;i<up;i++)c[i]=(c[i]+mod)%mod;
for(ll i=M-1;i<up;i++)c[i%(M-1)]+=c[i], c[i]=0;
}
inline void pow(ll *a, ll b)
{
for(ll i=0;i<up;i++)tmp[i]=a[i], ans[i]=0; ans[0]=1;
for(;b;b>>=1,juan(tmp,tmp,tmp))if(b&1)juan(ans,tmp,ans);
for(ll i=0;i<up;i++)a[i]=ans[i];
}
inline ll PR(ll x)
{
ll i, j;
for(i=2;i<x;i++)
{
for(j=1;j<=x-2;j++)if(pow(i,j,x)==1)break;
if(j==x-1 and pow(i,x-1,x)==1)return i;
}
}
void init()
{
ll i, L=0, t;
scanf("%lld%lld%lld%lld",&N,&M,&X,S);
for(i=1;i<=*S;i++)scanf("%lld",&t),t?S[t]=1:0;
for(up=1;up<=(M-2)<<1;up<<=1)L++;
for(i=0;i<up;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
pr=PR(M);
for(i=0;i<=M-2;i++)if(S[pow(pr,i,M)])trans[i]=1;
inv=pow(up,mod-2,mod);
}
void work()
{
ll i;
f[0]=1;
pow(trans,N);
juan(f,trans,f);
for(i=0;i<=M-2;i++)if(pow(pr,i,M)==X){printf("%lld",f[i]);break;}
}
int main()
{
init();
work();
return 0;
}