HDU 6125 - Free from square | 2017 Multi-University Training Contest 7
思路来自这里 - -
/* HDU 6125 - Free from square [ 分组,状压,DP ] | 2017 Multi-University Training Contest 7 题意: 不超过N的数字中选K个,其乘积不是平方数的倍数 限制 N,K <= 500 分析: 小于根号N的质因子至多只有8个,而大于根号N的质因子任意两个乘积大于N 所以N以内的完全平方数只有两种 1. 没有大于根号N的质因子 2. 有且只有1个大于根号N的质因子 对于小于根号N的质因子,可以直接按集合状压DP(自身为1组) 对于大于根号N的质因子,可以将包含该质因子的 非平方数的倍数的数 都归为1组,然后分组DP 每个数字的状态为其所包含的小于根号N的质因子的集合 具体DP递推式 为 if (a&b == 0) dp[k][a|b] = (dp[k][a|b] + dp[k-1][b]) % MOD; 其中 a, b为小于8的质因子的集合 */ #include <bits/stdc++.h> using namespace std; const int MOD = 1e9+7; const int N = 505; int p[8] = {2, 3, 5, 7, 11, 13, 17, 19}; int n, t, k; int dp[N][1<<10]; int st[N], belong[N]; vector<int> v[N]; int solve() { for (int i = 1; i <= n; i++) { belong[i] = i; for (int j = 0; j < 8; j++) if (i % p[j] == 0) { if (i% (p[j]*p[j]) == 0) { st[i] = -1; break; } st[i] |= 1<<j; belong[i] /= p[j]; } if (st[i] == -1) continue; if (belong[i] == 1) v[i].push_back(i); else v[belong[i]].push_back(i); } dp[0][0] = 1; for (int i = 1; i <= n; i++) { if (st[i] == -1 || v[i].size() == 0) continue; for (int l = k; l >= 1; l--) for (int j = 0; j < (1<<8); j++) for (auto & x : v[i]) { int p = st[x]; if (!(p&j)) dp[l][p|j] = (dp[l][p|j] + dp[l-1][j]) % MOD; } } int ans = 0; for (int i = 1; i <= k; i++) for (int j = 0; j < (1<<8); j++) ans = (ans + dp[i][j]) % MOD; return ans; } int main() { scanf("%d", &t); while (t--) { scanf("%d%d", &n, &k); memset(dp, 0, sizeof(dp)); memset(st, 0, sizeof(st)); for (int i = 1; i <= n; i++) v[i].clear(); printf("%d\n", solve()); } }
我自倾杯,君且随意