1. 程式人生 > >hdu 6125 Free from square (狀壓DP+分組背包)

hdu 6125 Free from square (狀壓DP+分組背包)

cto scan turn vector 選擇 == from ems include

題目大意:讓你在1~n中選擇不多於k個數(n,k<=500),保證它們的乘積不能被平方數整除。求選擇的方案數

因為質數的平方在500以內的只有8個,所以我們考慮狀壓

先找出在n以內所有平方數小於等於n的質數,然後我們把它們作為狀壓的狀態

然後要對每個小於n數進行狀壓,如果它不能被它能被質數的平方整除,那就篩出它所有的在狀態內的質因子,大於狀態內的質因子我們存到剩余因子的乘積的部分裏

比如46,它的狀態可以表示成0000 0001 (19,17,13,11,7,5,3,2) 46/2=23,把它存到23的0000 0001狀態裏

這麽做之後,你會驚奇的發現,每個數字只被存到了一個地方,且只被存了一次!

而這也是分組背包可行的關鍵

下一步就是分組背包了

我們從1遍歷到n,遍歷所有狀態,如果存了,意味著這個狀態表示的數可以選一個,就++

然後枚舉上一層進行轉移即可

細節可以看代碼

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <vector>
  5 #define N 100
  6 #define M 505
  7 #define maxn (1<<8)+5
  8 #define ll long long 
  9 #define mod 1000000007
 10
using namespace std; 11 12 int T,n,m,cnt,K; 13 ll f[2][M][maxn],tmp[M][maxn]; 14 int p[M],ok[M],pr[M],can[M][maxn],use[M]; 15 int a[]={0,2,3,5,7,11,13,17,19,5000}; 16 vector<int>to[M]; 17 void clr() 18 { 19 for(int i=0;i<M;i++) to[i].clear(); 20 memset(p,0,sizeof(p)); 21 memset(ok,0
,sizeof(ok)); 22 memset(pr,0,sizeof(pr)); 23 memset(can,0,sizeof(can)); 24 memset(use,0,sizeof(use)); 25 memset(f,0,sizeof(f)); 26 cnt=0; 27 } 28 void get_P() 29 { 30 for(int i=2;i<=n;i++) 31 { 32 if(!use[i]) pr[cnt++]=i; 33 for(int j=0;j<cnt&&i*pr[j]<=n;j++){ 34 use[i*pr[j]]=1; 35 if(i%pr[j]==0) break;} 36 } 37 for(int i=1;i<=n;i++) 38 { 39 int x=i,ps=0; 40 for(int j=0;j<min(K,8);j++) 41 if(x%(pr[j]*pr[j])==0) { 42 ok[i]=-1,p[i]=0; 43 break; 44 }else if(ok[i]!=-1&&x%pr[j]==0) 45 { 46 p[i]|=(1<<j); 47 x/=pr[j]; 48 } 49 if(ok[i]!=-1) 50 { 51 if(x!=1) to[x].push_back(i); 52 else to[i].push_back(i); 53 } 54 } 55 for(int i=1;i<=n;i++) //can數組表示i是否存了能用某個二進制質因子表示的數 56 { 57 if(ok[i]==-1) continue; 58 for(int j=0;j<to[i].size();j++) 59 can[i][p[to[i][j]]]=1; 60 } 61 } 62 63 int main() 64 { 65 //freopen("aa.in","r",stdin); 66 scanf("%d",&T); 67 while(T--) 68 { 69 scanf("%d%d",&n,&m); 70 clr(); 71 K=0; 72 while(K+1<=8&&a[K+1]*a[K+1]<=n) K++; 73 get_P(); 74 int y=1,x=0; 75 for(int i=1;i<=n;i++){ //分組背包 76 if(!to[i].size()) continue; 77 for(int s1=0;s1<(1<<K);s1++) 78 for(int j=1;j<=m;j++) 79 f[y][j][s1]=f[x][j][s1]; 80 for(int s1=0;s1<(1<<K);s1++) 81 { 82 if(!can[i][s1]) continue; 83 f[y][1][s1]=(f[y][1][s1]+1)%mod; 84 //如果i的狀態裏有s1,那麽說明這個狀態表示的數可以直接被選,就++ 85 for(int j=1;j<=m;j++) 86 for(int s2=0;s2<(1<<K);s2++) 87 { 88 if(s1&s2) continue; 89 f[y][j][s1|s2]+=f[x][j-1][s2]; 90 f[y][j][s1|s2]%=mod; 91 } 92 } 93 swap(y,x); 94 } 95 ll ans=0; 96 for(int s=0;s<(1<<K);s++) 97 for(int j=1;j<=m;j++) 98 ans=(ans+f[x][j][s])%mod; 99 printf("%lld\n",ans); 100 } 101 return 0; 102 }

hdu 6125 Free from square (狀壓DP+分組背包)