hdu6125-(状态压缩+分组背包)

题解:因为小于根号500的质数有8个,我们可以用二进制表示为放入的这些数已经含有前面8个质数的哪几个然后,因为选择1-k个数后相乘起来没有平方因子,所以有任何能除以这前面8个质数的平方的都不可以,还有就是如果这个数把这8个质数能取余等0的都除后等于1的话那么这个数应该在自己这个数这一组,如果不能等于1的话应该再除以后剩下的那一组,为什么呢?因为除以后就剩下它的结果很显然只剩下一个质数了,那么这些数不能同出现,所以分到同一组里面,如果等于1的话这些数可能同时出现,也可能不同时出现,但是可以用二进制表现出来,最后用二维dp来做那么转移方程就是:

if((k&d)==0) dp[j+1][k|d] = (dp[j+1][k|d]+dp[j][k])%mod;(j表示放了j个数,k,d用二进制表示含有前面8个质数的哪几个)            

#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long int ll;
const ll mod = 1e9+7;
const int mx = 505;
int st[mx],belong[mx];
ll dp[mx][mx];
vectorv[mx];
int p[] = {2,3,5,7,11,13,17,19};
ll work(int n,int m){
    memset(dp,0,sizeof(dp));
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++)
        v[i].clear(),belong[i] = i,st[i] = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 0; j < 8; j++)
            if(st[i]!=-1&&i%p[j] == 0&&i%(p[j]*p[j])!=0)
                st[i] |= 1<= 0; j--)
            for(int k = 0; k < (1<<8); k++)
                for(int l = 0; l < v[i].size(); l++){
                    int d = st[v[i][l]];
                    if((k&d)==0)
                        dp[j+1][k|d] = (dp[j+1][k|d]+dp[j][k])%mod;
                }
    }
    ll ans = 0;
    for(int i = 1; i <= m; i++)
        for(int j = 0; j <(1<<8); j++)
            ans = (ans+dp[i][j])%mod;
    return ans%mod;
}
int main(){
    int n,m;
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        printf("%I64d\n",work(n,m));
    }
    return 0;
}


你可能感兴趣的:(DP)