首先将度数 \(-1\)。
设 \(f_i\) 为体积为 \(i\) 至多能用几个物品凑出来,\(g_i\) 为至少。
我们现在要证明一个东西:\(x \in [g_i, f_i]\),\((i, x)\) 合法。
首先若 \((s, x)\) 合法,那么必须满足 \(s - x \in [-z, z - 2]\),其中 \(z = \sum\limits_{i=1}^n [d_i = 0]\)。
因为 \(s - x = \sum\limits_{y \in S} (y - 1)\),最小值就是把 \(< 1\) 的值加起来,最大值就是把 \(\ge 1\) 的值加起来。
然后 \([g_i, g_i + z]\) 一定能通过最少的方案后面加若干 \(0\) 凑出来,\([f_i - z, f_i]\) 同理。
那么只要满足 \(g_i + z + 1 \ge f_i - z\) 即 \(f_i - g_i \le 2z + 1\) 即可。
\((i, f_i)\) 和 \((i, g_i)\) 合法,那么 \(i - f_i \in [-z, z - 2], i - g_i \in [-z, z - 2]\),所以它们的差 \(\in [0, 2z - 2]\),得证。
和为 \(n\) 的数中至多有 \(O(\sqrt{n})\) 种不同的数。枚举这 \(n\) 个数,做多重背包即可。肯定不能直接朴素做,容易按照同余系分开考虑,单调队列优化。
code
// Problem: F - Tree Degree Subset Sum
// Contest: AtCoder - AtCoder Regular Contest 125
// URL: https://atcoder.jp/contests/arc125/tasks/arc125_f
// Memory Limit: 1024 MB
// Time Limit: 6000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 200100;
int n, a[maxn], f[maxn], g[maxn], h[maxn], q[maxn], b[maxn];
void solve() {
scanf("%d", &n);
for (int i = 1, u, v; i < n; ++i) {
scanf("%d%d", &u, &v);
++a[u];
++a[v];
}
mems(f, -0x3f);
mems(g, 0x3f);
for (int i = 1; i <= n; ++i) {
--a[i];
++b[a[i]];
}
f[0] = g[0] = 0;
for (int i = 1; i <= n; ++i) {
if (!b[i]) {
continue;
}
for (int j = 0; j <= n; ++j) {
h[j] = f[j];
}
for (int j = 0; j < i; ++j) {
int hd = 1, tl = 0;
for (int k = j; k <= n; k += i) {
while (hd <= tl && q[hd] < k - i * b[i]) {
++hd;
}
if (hd <= tl) {
f[k] = max(f[k], h[q[hd]] + (k - q[hd]) / i);
}
while (hd <= tl && h[q[tl]] - q[tl] / i <= h[k] - k / i) {
--tl;
}
q[++tl] = k;
}
}
}
for (int i = 1; i <= n; ++i) {
if (!b[i]) {
continue;
}
for (int j = 0; j <= n; ++j) {
h[j] = g[j];
}
for (int j = 0; j < i; ++j) {
int hd = 1, tl = 0;
for (int k = j; k <= n; k += i) {
while (hd <= tl && q[hd] < k - i * b[i]) {
++hd;
}
if (hd <= tl) {
g[k] = min(g[k], h[q[hd]] + (k - q[hd]) / i);
}
while (hd <= tl && h[q[tl]] - q[tl] / i >= h[k] - k / i) {
--tl;
}
q[++tl] = k;
}
}
}
ll ans = 0;
for (int i = 0; i <= n; ++i) {
if (f[i] >= 0) {
ans += f[i] - g[i] + 1 + b[0];
}
}
printf("%lld\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
- AtCoder Regular Contest Degree Subsetatcoder regular contest degree atcoder contest subset grand atcoder regular contest 165 minimization atcoder regular contest atcoder regular contest 166 atcoder regular contest 167 atcoder regular contest sliding atcoder regular contest builder atcoder regular contest 164 subsegments atcoder regular contest