Live2d Test Env

HDU - 6125: Free from square (状压DP+分组背包)

problem:给定N,K。表示你有数1到N,让你最多选择K个数,问有多少种方案,使得选择的数的乘积无平方因子数。N,K<500;

solution:显然可以状压DP做,但是500以内的素数还是蛮多的,无法高效得DP。   但是我们注意到,大于sqer(N)的素数,同一类最多用一个,这不就是分组背包吗。

所以我们只有小于sqrt(N)的素数用常规的DP,否则用分组背包。 dp[i][j]表示选择了i个数,其中小于sqrt(N)的素数状态为j。 j<(1<<8);

分组背包:我们把个数放在第一维,就保证了同一类的物品不相互影响,做到了分组。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int Mod=1e9+7;
const int maxn=510;
int dp[maxn][256],a[maxn],x[maxn];
int p[]={2,3,5,7,11,13,17,19};
vector<int>G[maxn];
void init(int N)
{
    rep(i,1,N) G[i].clear();
    rep(i,1,N){
        int t=i,tmp=0;
        rep(j,0,7){
            if(t%(p[j]*p[j])==0) {
                t=-1;
                break;
            }
            if(t%p[j]==0) {
                t/=p[j];
                tmp|=(1<<j);
            }
        }
        if(t!=-1) {
            G[t].push_back(i);
            x[i]=tmp;
        }
    }
}
int main()
{
    int N,T,K,ans;
    scanf("%d",&T);
    while(T--){
        memset(dp,0,sizeof(dp));
        dp[0][0]=1; ans=0;
        scanf("%d%d",&N,&K);
        init(N);
        rep(j,0,G[1].size()-1) //1单独考虑
          for(int v=K-1;v>=0;v--){
             int t=G[1][j];
             rep(k,0,255){
                if((x[t]&k)==0){
                    (dp[v+1][k|x[t]]+=dp[v][k])%=Mod;
                }
            }
        }
        rep(i,2,N){
            if(G[i].size()==0) continue;
            for(int v=K-1;v>=0;v--) //放在第一维保证了分组
             rep(j,0,G[i].size()-1){
                int t=G[i][j];
                rep(k,0,255){
                    if((x[t]&k)==0){
                        (dp[v+1][k|x[t]]+=dp[v][k])%=Mod;
                    }
                }
            }
        }
        rep(j,1,K)
         rep(i,0,255) (ans+=dp[j][i])%=Mod;
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2019-05-01 14:51  nimphy  阅读(246)  评论(0编辑  收藏  举报