hdu 6125 Free from square(状压dp+分组背包)
题目链接:hdu 6125 Free from square
题意:
从不大于n的所有正整数中选出至少1个且至多k个使得乘积不包含平方因子。
题解:
很容易想到,选出来的数所包含的每个质因子只能有一个。
那么我们对所有的质因子进行状压dp,500以内大概有100个质因子。
那么就成了2100,显然不能接受。
然后我们发现,对于每一个x(x<=n),x最多只包含一个大于sqrt(n)的质因子。
然后我们就可以将大于sqrt(n)的x按照x所包含的那个质因子分组。
比如 n=500,现在有53 54 55 56 57...114。那么53和106在一组,57和114在一组。(可能还有其他的组,这里我随便举例)
在同一组里每个数只能选一次,所以就是分组背包。
所以我们就可以将该问题拆成两个部分:
1.小于sqrt(n)的部分进行状压dp。
2.大于sqrt(n)的部分进行分组背包。
最后将所有合法的答案加起来就行了。(ps:注意处理1这个数字和过滤掉含有多个质因子的数)
1 #include<bits/stdc++.h> 2 #define mst(a,b) memset(a,b,sizeof(a)) 3 #define F(i,a,b) for(int i=(a);i<=(b);++i) 4 using namespace std; 5 6 const int N=505,P=1e9+7,U=(1<<8)-1; 7 int t,n,K; 8 int p[8]={2,3,5,7,11,13,17,19}; 9 int dp[2][N][1<<8],st[N],cur; 10 vector<int>v[N],small; 11 12 inline void up(int &a,int b){a+=b;if(a>P)a-=P;} 13 14 void solve() 15 { 16 mst(dp,0),cur=0; 17 mst(st,0),small.clear(); 18 F(i,1,n)v[i].clear(); 19 F(i,1,n) 20 { 21 int flag=1,tmp=i; 22 F(j,0,7) 23 { 24 if(tmp%(p[j]*p[j])==0){flag=0;break;} 25 else if(tmp%p[j]==0) 26 { 27 tmp/=p[j],st[i]|=1<<j; 28 } 29 } 30 if(tmp==1&&flag)small.push_back(i); 31 else if(flag)v[tmp].push_back(i); 32 } 33 dp[cur][0][0]=1,dp[cur][1][0]=1;//考虑1这个数 34 int sz=small.size();//先处理只含小因子的数 35 F(k,1,sz-1)F(i,0,K-1)F(j,0,U)if(dp[cur][i][j]) 36 { 37 int x=small[k]; 38 if(j&st[x])continue; 39 up(dp[cur][i+1][j|st[x]],dp[cur][i][j]); 40 } 41 F(i,1,n)//将含大因子的数进行分组背包 42 { 43 if(v[i].size()==0)continue; 44 cur^=1,memcpy(dp[cur],dp[cur^1],sizeof(dp[cur])); 45 F(j,0,K-1)F(k,0,U)if(dp[j][k]) 46 { 47 for(auto &it:v[i]) 48 { 49 if(k&st[it])continue; 50 up(dp[cur][j+1][k|st[it]],dp[cur^1][j][k]); 51 } 52 } 53 } 54 int ans=0; 55 F(i,1,K)F(j,0,U)up(ans,dp[cur][i][j]); 56 printf("%d\n",ans); 57 } 58 59 int main(){ 60 scanf("%d",&t); 61 while(t--)scanf("%d%d",&n,&K),solve(); 62 return 0; 63 }