题目大意
n*m的矩形,从第一行开始依次取数。
第一行与最后一行只能取一个数,其余行可以取1~2个数。
问有多少种取数顺序使得所取的数乘积被p整除,答案模mo。
DP
我们想要知道乘积能否被p整除,所关心得只是乘积与p的gcd。
找出p的所有因子,可知其因子数量是三位数级别。
然后设f[i,j]表示前i行取了一些数使得乘积与p的gcd是p的第j个因子的方案。可以先在每一行内做,再进行转移。
当然,我们需要trans[i,j]表示p的第i个因子与第j个因子的乘积与p的gcd。
然后题目解决,详见代码。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=200+10;
int f[maxn][1000],now[1000],cnt[1000],a[1000],b[200010];
int trans[1000][1000],c[maxn][10010];
int i,j,k,l,t,n,m,p,mo,top;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int main(){
//freopen("1172.in","r",stdin);
scanf("%d%d",&n,&m);
scanf("%d%d",&p,&mo);
fo(i,1,p)
if (p%i==0){
a[++top]=i;
b[i]=top;
}
fo(i,1,top)
fo(j,1,top)
trans[i][j]=b[gcd((int)((ll)a[i]*a[j]%p),p)];
fo(i,1,n)
fo(j,1,m)
scanf("%d",&c[i][j]);
f[0][1]=1;
fo(i,1,n){
fo(j,1,top) cnt[j]=0;
fo(j,1,m) (cnt[b[gcd(c[i][j],p)]]+=1)%=mo;
fo(j,1,top) now[j]=cnt[j];
if (1<i&&i<n){
fo(j,1,top)
fo(k,1,top)
if (j==k) (now[trans[j][k]]+=cnt[j]*(cnt[j]-1)%mo)%=mo;
else (now[trans[j][k]]+=cnt[j]*cnt[k]%mo)%=mo;
}
fo(j,1,top)
fo(k,1,top)
(f[i][trans[j][k]]+=f[i-1][j]*now[k]%mo)%=mo;
}
printf("%d\n",f[n][top]);
}