bzoj 4197 [Noi2015]壽司晚宴 狀壓dp
阿新 • • 發佈:2018-12-11
題面
解法
思路是真的神仙
- 考慮當比較小的時候怎麼做,因為質因子的個數不多,所以將是否取質因子的狀態壓成一個二進位制數然後轉移即可
- 然後發現,質因子個數還是比較多的
- 那麼我們對於每一個數都分開考慮,可以發現最多隻會有一個質因子
- 計算一下在左右,在這個範圍內的質數很少,只有8個
- 那麼我們可以將這些質因子的取值狀態壓成一個二進位制數,設表示第一個人選擇了質因子集合,第二個人選擇了質因子集合的方案數
- 然後做法可以說就比較顯然了:對於每一個數先求出大於的質因子,然後按照這個排序,將大於的質因子相同的放入一組。對於同一組裡的數,我們可以進行這樣一個dp:設表示這個因子給第一個人,表示這個因子給第二個人的方案數。轉移十分顯然,這裡就不必再贅述了
- 考慮將同一組裡的全部求解完畢之後怎麼把答案合併到上,因為在計算的時候會重複計算兩個人都不選擇這個質因子的情況,所以
- 因為這裡開的是類似於滾動陣列的東西,所以在求解的時候下標應該倒著列舉,類似於0/1揹包
- 時間複雜度:
程式碼
#include <bits/stdc++.h>
#define N 550
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int prime[9] = {0, 2, 3, 5, 7, 11, 13, 17, 19};
int f1[N][N], f2[N][N], ans[N][N];
struct Info {
int s, key;
bool operator < (const Info &a) const {return key < a.key;}
void divide(int x) {
for (int i = 1; i <= 8; i++) {
if (x % prime[i] != 0) continue;
s |= 1 << i - 1;
while (x % prime[i] == 0) x /= prime[i];
}
key = x;
}
} a[N];
int main() {
int n, Mod; read(n), read(Mod);
for (int i = 2; i <= n; i++) a[i].divide(i);
sort(a + 2, a + n + 1); ans[0][0] = 1;
for (int i = 2; i <= n; i++) {
if (a[i].key == 1 || a[i].key != a[i - 1].key || i == 2)
memcpy(f1, ans, sizeof(f1)), memcpy(f2, ans, sizeof(f2));
for (int j = 255; ~j; j--)
for (int k = 255; ~k; k--) {
if ((j & k) > 0) continue;
if ((k & a[i].s) == 0) f1[j | a[i].s][k] = (f1[j | a[i].s][k] + f1[j][k]) % Mod;
if ((j & a[i].s) == 0) f2[j][k | a[i].s] = (f2[j][k | a[i].s] + f2[j][k]) % Mod;
}
if (i == n || a[i].key == 1 || a[i].key != a[i + 1].key)
for (int j = 0; j <= 255; j++)
for (int k = 0; k <= 255; k++) {
if ((j & k) > 0) continue;
ans[j][k] = ((f1[j][k] + f2[j][k]) % Mod - ans[j][k] + Mod) % Mod;
}
}
int ret = 0;
for (int i = 0; i <= 255; i++)
for (int j = 0; j <= 255; j++)
if ((i & j) == 0) ret = (ret + ans[i][j]) % Mod;
cout << ret << "\n";
return 0;
}