51nod 1354 选数字【DP】

Description

当给定一个序列a[0],a[1],a[2],…,a[n-1] 和一个整数K时,我们想找出,有多少子序列满足这么一个条件:把当前子序列里面的所有元素乘起来恰好等于K。

对于第一个数据,我们可以选择[3]或者[1(第一个1), 3]或者[1(第二个1), 3]或者[1,1,3]。所以答案是4。

题解

可以发现,m的因数个数很少,所以可以定义 f[i][j] 表示前 i 个数,乘起来等于m的第j个因数的方案数。转移的时候可以借助map实现。

代码

#include
#include
#include
#include
#include
#define maxn 1006
#define maxm 10000006
#define maxs 3200
#define tt 1000000007
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
int T,n,m,a[maxn],b[maxn],f[maxn][maxs];
map < int,int >p;
int main(){
    freopen("math.in","r",stdin);
    freopen("math.out","w",stdout);
    T=_read();
    for(int t=1;t<=T;t++){
        n=_read();m=_read();p.clear();
        for(int i=1;i<=n;i++)a[i]=_read();
        int b0=0;
        for(int i=2;i<=sqrt(m);i++)if(!(m%i))b[++b0]=i;
        for(int i=sqrt(m);i>=1;i--)if(i*i!=m&&!(m%i))b[++b0]=m/i;
        for(int i=1;i<=b0;i++)p[b[i]]=i;p[1]=0;b[0]=1;
        memset(f,0,sizeof(f));
        f[0][0]=1;
        for(int i=1;i<=n;i++)
         for(int j=0;j<=b0;j++)
          if(!(b[j]%a[i])&&p.find(b[j]/a[i])!=p.end())f[i][j]=(f[i-1][j]+f[i-1][p[b[j]/a[i]]])%tt;
                                         else f[i][j]=f[i-1][j];
        printf("%d\n",f[n][b0]);
    }
}

你可能感兴趣的:(-----DP-----,51nod)