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; }
It is your time to fight!