9
1001 樹異或價值 (hdu7529)
對價值定義式進行轉化,\((a_i\oplus a_j)\times lca(a_i, a_j)\) 可視為 \(a_i,a_j\) 的所有祖先下 \(\sum a_i\oplus a_j\),陣列 \(a\) 總價值即各節點子樹中任意兩個子節點的異或之和。由按位異或的性質,不同二進位制位之間答案互不影響,可簡化考慮最低位即 \(a_i\in \{0, 1\}\),計算出此時的 \(ans'\),有 \(ans = (ans')^k\).
\(a_i\in \{0, 1\}\) 時,為了使異或之和儘可能大,即各節點子樹內 \((\sum [a=0])\times (\sum [a=1])\) 最大,應使 \(a = 0\) 與 \(a = 1\) 子節點數量儘可能平均;實際上透過構造可實現所有節點下 \(\mid\sum[a = 0] - \sum[a = 1])\mid\space \leq 1\). 設 \(dp_x\) 為 \(a_x = 0\) 時子樹符合條件的答案數,\(y\in subtree(x)\),\(se_x,so_x\)分別代表大小為偶數/奇數的 \(y\) 數量,則有:
\(dp_x =\prod dp_y \times 2^{se_x}\times (\dbinom{so_x}{\lfloor\frac{so_x}{2}\rfloor} + \dbinom{so_x}{\frac{so_x}{2} - 1}\times [2|so_x])\).
最終答案即 \((2\times dp_1)^k\). 組合數學的邏輯多少有點抽象,建個模理解一下式子就好,至少程式碼不難寫。
void pre(int i) {
sz[i] = 1;
s0[i] = s1[i] = 0;
for(int j = 0; j < v[i].size(); j++) {
int t = v[i][j];
pre(t);
sz[i] += sz[t];
if(sz[t] & 1) s1[i]++;
else s0[i]++;
}
}
void cal(int i) {
dp[i] = 1;
for(int j = 0; j < v[i].size(); j++) {
int t = v[i][j];
cal(t);
dp[i] *= dp[t];
dp[i] %= mo;
}
dp[i] *= fp(2, s0[i]);
dp[i] %= mo;
dp[i] *= (c(s1[i], s1[i] / 2) + ((s1[i] & 1) == 0) * c(s1[i], s1[i] / 2 - 1)) % mo;
dp[i] %= mo;
}
1003 黑洞合併 (hdu7531)
真抽象啊,這個《規律》是賽時我發現了都不敢提出來的程度。把它寫這裡純粹是為了紀念我寫過這麼一道抽象的題目()
1006 融合礦石 (hdu7534)
非常巧妙的dp. 資料保證隨著金輝石佔比的增加,礦石價值單調不減,且礦石的數量無限,因此用完全揹包預處理出所有可能的融合情況下、一定質量礦石的最大金輝石含量,將結果看作 \(m\) 種不同的新礦石,再用一次完全揹包計算即可。
for(int i = 1; i <= n; i++) {
int a, b;
scanf("%d%d", &a, &b);
for(int j = a; j <= m; j++) {
f[j] = max(f[j], f[j - a] + b);
}
}
for(int i = 1; i <= m; i++) {
if(!f[i]) continue;
int x = (f[i] * 10 - 1) / i + 1;
f[i] = v[x] * i;
}
for(int i = 1; i <= m; i++) {
for(int j = i; j <= m; j++) {
g[j] = max(g[j], g[j - i] + f[i]);
}
}
printf("%lld\n", g[m]);