题目传送门(内部题45)
输入格式
第一行$3$个整数$n,m,P$。
第二行$m$个整数,表示$m$次询问。
输出格式
一行$m$个整数表示答案。
样例
样例输入1:
2 4 4
0 1 2 3
样例输出1:
8 2 4 2
样例输入2:
3 3 2333333
1 38 227
样例输出2:
578724625 893056135 887007020
数据范围与提示
样例1解释:
对于第三组询问,四种方案分别为:
$x_0=1,x_1=2$
$x_0=2,x_1=1$
$x_0=2,x_1=3$
$x_0=3,x_1=2$
数据范围:
对于前$20\%$的数据:$n=2$
对于另$30\%$的数据:$Pleqslant 1,000$
对于另$10\%$的数据:$P$为质数。
对于所有数据:
$1leqslant nleqslant 50$
$1leqslant mleqslant 1,000$
$2leqslant Pleqslant {10}^9$
$0leqslant v_i<P$
题解
首先,我们考虑$DP$,定义$dp[i][j]$表示前$i$个变量乘起来取模后结果为$j$的方案数,转移挺简单的,不再赘述。
但是显然跑不过,考虑优化。
打表找规律发现,如果$gcd(a,P)=gcd(b,P)$那么$dp[i][a]=dp[i][b]$。
具体我也不会证,这不怪我,因为出题人也不会。
这样状态数就减少了不少,转移大同小异,注意乘上$sigma(x)$($x$的约数个数),最后再除掉即可。
直接算可能会$TLE$,我们可以先预处理出来每两个数乘起来是几,注意用$map$即可。
时间复杂度:$Theta(nsigma(P)^2)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long P;
int Map[1345][1345];
long long dp[51][1345];
long long que[1345],phi[1345];
map<int,int> s;
long long qpow(long long x,long long y)
{
long long res=1;
while(y)
{
if(y&1)res=res*x%1000000007;
x=x*x%1000000007;
y>>=1;
}
return res;
}
void pre_work()
{
for(int i=1;i*i<=P;i++)
if(!(P%i))
{
que[++que[0]]=i;
if(i*i!=P)que[++que[0]]=P/i;
}
}
long long get_phi(long long x)
{
long long res=x;
for(long long i=2;i*i<=x;i++)
if(!(x%i))
{
res=res/i*(i-1);
while(!(x%i))x/=i;
}
if(x>1)res=res/x*(x-1);
return res;
}
int main()
{
scanf("%d%d%lld",&n,&m,&P);
pre_work();
for(int i=1;i<=que[0];i++)
{
phi[i]=get_phi(P/que[i]);
s[que[i]]=i;
dp[1][i]=phi[i];
}
for(int i=1;i<=que[0];i++)
for(int j=1;j<=que[0];j++)
Map[i][j]=s[__gcd(que[i]*que[j]%P,P)];
for(int i=2;i<=n;i++)
for(int j=1;j<=que[0];j++)
for(int k=1;k<=que[0];k++)
dp[i][Map[j][k]]=(dp[i][Map[j][k]]+dp[i-1][j]*phi[k]%1000000007)%1000000007;
while(m--)
{
long long x;
scanf("%lld",&x);
int gcd=__gcd(P,x);
printf("%lld ",dp[n][s[gcd]]*qpow(phi[s[gcd]],1000000005)%1000000007);
}
return 0;
}
rp++