[bzoj1172]Dream

题目大意

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]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值