题意
题解
对于非叶子节点,若 n A < n B nA < nB nA<nB,令其状态为 A A A,反之为 B B B。那么两个用户对应流量的费用可以拆分为单个用户的贡献,即当用户 v v v 与其祖先节点 u u u 状态相同时, v v v 贡献为 v → u v\rightarrow u v→u 的流量。
祖先节点的状态依赖于叶子节点的状态,而叶子节点的贡献依赖于祖先节点的状态。那么先自顶而下地枚举祖先节点的状态,在自底向上地通过树形 DP 进行合并。
根据主定理,总时间复杂度 O ( n 2 log n ) O(n^2\log n) O(n2logn)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int MAXV = 1 << 10, SZ = MAXV << 1;
constexpr ll INF = 0x3f3f3f3f3f3f3f3f;
int N, A[MAXV], C[MAXV];
int F[MAXV][MAXV];
int sum[MAXV][MAXV + 1];
ll rec[SZ][MAXV];
void _min(ll &x, ll y) { x = min(x, y); }
void solve(int k, int l, int r, int d, int s)
{
if (r - l == 1)
{
rec[k][0] = A[l] ? C[l] : 0;
rec[k][1] = A[l] ? 0 : C[l];
int _l = 0, _r = 1 << N;
for (int i = 0; i < d; ++i)
{
int _m = (_l + _r) / 2;
int d;
if (l < _m)
{
d = sum[l][_r] - sum[l][_m];
_r = _m;
}
else
{
d = sum[l][_m] - sum[l][_l];
_l = _m;
}
rec[k][s >> i & 1] += d;
}
return;
}
for (int i = 0; i <= r - l; ++i)
rec[k][i] = INF;
int m = (l + r) / 2, chl = k * 2 + 1, chr = k * 2 + 2;
solve(chl, l, m, d + 1, s | 1 << d);
solve(chr, m, r, d + 1, s | 1 << d);
int len = (r - l) / 2;
for (int na = 0; na < r - l - na; ++na)
for (int nl = max(0, na - len); nl <= min(len, na); ++nl)
_min(rec[k][na], rec[chl][nl] + rec[chr][na - nl]);
solve(chl, l, m, d + 1, s);
solve(chr, m, r, d + 1, s);
for (int na = len; na <= r - l; ++na)
for (int nl = max(0, na - len); nl <= min(len, na); ++nl)
_min(rec[k][na], rec[chl][nl] + rec[chr][na - nl]);
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> N;
int n = 1 << N;
for (int i = 0; i < n; ++i)
cin >> A[i], A[i] ^= 1;
for (int i = 0; i < n; ++i)
cin >> C[i];
for (int i = 0; i < n; ++i)
for (int j = 1; i + j < n; ++j)
cin >> F[i][i + j], F[i + j][i] = F[i][i + j];
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
sum[i][j + 1] = sum[i][j] + F[i][j];
solve(0, 0, n, 0, 0);
ll res = INF;
for (int i = 0; i <= n; ++i)
_min(res, rec[0][i]);
cout << res << '\n';
return 0;
}