这道题调了大概一个多小时终于过了,有许多地方我希望记清楚,我还是记下来。
先给上代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath> 6 using namespace std; 7 const int N = 3003; 8 int n; 9 int prime[N],pd,t,record[N]; 10 bool vis[N]; 11 int small[N],ts,tot; 12 int big[N],tb; 13 int biao[603][1003],size[1003]; 14 long long dp[(1<<16)+10][603]; 15 void dfs(int i,int linshi,int t,long long kk) { 16 if(t > ts) return; 17 if(kk*small[t]<=n) { 18 biao[i][++size[i]] = linshi+(1<<(t-1)); 19 dfs(i,linshi+(1<<(t-1)),t+1,kk*small[t]); 20 dfs(i,linshi,t+1,kk); 21 } 22 } 23 inline void solve(int n) { 24 int k; 25 for(int s = 2 ; s <= n ; ++s) { 26 if(!vis[s]) prime[++t] = s; 27 for(int j = 1 ; j <= t && (k=prime[j]*s)<=n ; ++j) { 28 vis[k] = true; 29 if(s%prime[j]==0) break; 30 } 31 } 32 for(int i = 1 ; i <= n ; ++i) { 33 int linshi = i; 34 for(int j = 1 ; j <= t && linshi!=1 ; ++j) { 35 while(linshi%prime[j]==0 && linshi!=1) { 36 linshi/=prime[j]; 37 record[prime[j]]++; 38 } 39 } 40 } 41 pd = sqrt(n); 42 for(int i = 1 ; i <= n ; ++i) { 43 if(record[i]%2==1) { 44 if(i<pd) small[++ts] = i; 45 else big[++tb] = i; 46 } 47 } 48 tot = ts+tb; 49 for(int i = 1 ; i <= ts ; ++i) dfs(i,(1<<(i-1)),i+1,small[i]),biao[i][++size[i]] = (1<<(i-1)); 50 for(int i = 1 ; i <= tb ; ++i) dfs(i+ts,0,1,big[i]); 51 int end = (1<<ts)-1; 52 dp[end][1] = 1; 53 for(int i = 1 ; i <= tot ; ++i) { 54 for(int j = 0 ; j <= end ; ++j) { 55 if(dp[j][i]==0) continue; 56 dp[j][i+1] = dp[j][i]; 57 for(int k = 1 ; k <= size[i] ; ++k) 58 if((biao[i][k]&j)==biao[i][k]) dp[j-biao[i][k]][i+1] += dp[j][i]; 59 } 60 } 61 printf("%lld",dp[0][tot]*2); 62 } 63 int main() { 64 freopen("test.in","r",stdin); 65 freopen("test.out","w",stdout); 66 scanf("%d",&n); 67 solve(n); 68 }
首先感觉那个DFS是可以直接做的,就是可以直接转移的,当时受了同学的蛊惑我于是打了个转移表。反正还是蛮优的,然后这道题重点就是DP了,最开始我只想考虑大质数的时候是可能有问题的。所以我把小质数加上来一起搞,可以处理出只选大质数的时候的方案。代码还是可以好好感受感受在最后一步J到大质数J+1的时候,我就可以知道集合s为各个状态时I的方案,然后就可以累加起来咯。
隔两天再看看,别忘了蛤蛤!