BZOJ 3425: Poi2013 Polarization
最小值就一个点全部是出边,相邻的点都是入边,这么一直排下去,答案就是 \(n-1\)
最大值就选重心,每个子树要么全是出边要么全是入边
每个子树里的答案已经确定了,子树之间若有 \(x\) 个是出边,就有 \(n-x-1\) 个是入边
经过重心的答案就是 \(x(n-x-1)\)
相当于有子树个物品,每个物品的大小为它们子树对应的 size
然后做一遍背包,如果用bitset能优化到 \(O(\dfrac{n^2}{32})\)
考虑到大于 \(\sqrt n\) 的物品最多只有 \(\sqrt n\) 个,那么直接暴力dp
小于 \(\sqrt n\) 的进行二进制分组
复杂度就是 \(O(\dfrac{n \sqrt n}{32})\)
#include <bits/stdc++.h>
#define ll long long
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline char getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read() {
int x = 0, f = 1; char ch = getc();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getc(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getc(); }
return x * f;
}
const int N = 250007;
std::bitset<N> dp;
int n, head[N], cnt = 1, to[N * 2], ne[N * 2], sz[N], maxsz[N], root;
int w[N];
ll f[N];
inline void add(int u, int v) {
to[++cnt] = v; ne[cnt] = head[u]; head[u] = cnt;
}
void dfs(int u, int fa) {
sz[u] = 1; f[u] = 0; maxsz[u] = 0;
for (int i = head[u]; i; i = ne[i]) {
int v = to[i];
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
f[u] += f[v] + sz[v];
if (sz[v] > maxsz[u]) maxsz[u] = sz[v];
}
maxsz[u] = std::max(maxsz[u], n - sz[u]);
if (maxsz[u] < maxsz[root]) root = u;
}
int main() {
n = read();
for (int i = 1, u, v; i < n; i++)
u = read(), v = read(), add(u, v), add(v, u);
maxsz[0] = n;
dfs(1, 0);
dfs(root, 0);
dp[0] = 1;
int sqr = sqrt(n);
for (int i = head[root]; i; i = ne[i]) {
int v = to[i];
if (sz[v] > sqr) dp |= dp << sz[v];
else w[sz[v]]++;
}
for (int i = 1; i <= sqr; i++) if (w[i])
for (int j = w[i], k = 1; j; j -= k, k = std::min(j, k << 1))
dp |= dp << (i * k);
ll ans = 0;
for (int i = n / 2; ~i; i--)
if (dp[i]) { ans = f[root] + 1LL * i * (n - i - 1); break; }
printf("%d %lld\n", n - 1, ans);
return 0;
}

浙公网安备 33010602011771号