题意
给n个节点的树,边权
L[x][y]
,定义一条边
xy∈S≠∅
当且仅当
R[x]+R[y]≥L[x][y]
,现在要给每个节点x确定
R[x]≥0
,使得
∑R[x]∑L[x][y]⋅[xy∈S]
最小。
n<=30,0<=L[x][y]<=1e9
题解
令
∑R[x]∑L[x][y]⋅[xy∈S]≤λ
,
即要求是否存在:
∑R[x]−∑(λL[x][y])⋅[xy∈S]≤0
即要使:
min{∑R[x]−∑(λL[x][y])⋅[xy∈S]}≤0
注意到:
R[x]+R[y]≥L[x][y]
前提下,
∑R[x]
实质是最小顶标和,而树是二分图,最小顶标和转化为最大权匹配。
即枚举图,确定 [xy∈S] 的边,在新图中做最大权匹配,得到 min∑R[x] 。
考虑到树的最大权匹配能 O(n) 求得,直接做复杂度是 2n×n 不能过。解决方法是利用树的性质,找出质心,分隔树,易知分隔得到的子树大小 ≤n/2 ,可行。
对每棵子树,计算 f[u][0] 与 f[u][1] 代表 u 这个点是否被匹配情况下,u这棵子树的最大权匹配。
接下来这题最恶心的地方在于如何合并,即如何保证,或者说按照什么顺序合并能够使得保证合并得到的值,在情况合法(满足整体最大权匹配)的前提下的最小目标函数值。
(这段是错误解法,不希望被误导的读者请自行跳过。。。。)
首先想到的想法是对每个子树只记录三个值:
接下来是合并顺序的问题。
按照w排序直接做?错,比如说
A B 3
B C 2
A D 2
每个子树选的时候,A-B是最大匹配边,而当两个连起来的时候,B-C A-D成了最大匹配边
那么按照dp11-dp0排序?也是错的。
同样是上面的例子,这时候B子树如果不匹配A-B这条边,子树中最优情况是不找匹配边,而此时A-D连起来时,A-B反而又成了匹配边。。。
这个问题引出:如果不记录子树内选边情况,单记录是否与跟这条边相连是不够的。。。
QwQ
正确的做法是:对每个
root
连出去的点
u
,计算
那么观察
root
,假设它要用某个
u
,在其选边情况为
故,我们把所有的 gu,S 排序,从小到大依次计算目标函数,
注意到这题只要找出是否存在 ≤0 的方案,所以若存在目标函数 ≤0 的情况则返回 True ,否则 False
在省状态之前要考虑清楚状态之间的联系,在计算目标的时候实质上是要保证两点:
1. 尽可能保证缩减状态,通常利用最优性质(即类似最优性剪枝),使目标状态分割为多个子状态的最优值,再合并
2. 要保证不能引入非法状态。
(做了一天终于搞定了QwQ)
code
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
using namespace std;
typedef long double LD;
typedef long long LL;
const int maxn = 40;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
const LD eps = 1e-15;
int n, rt;
int tot, st[maxn];
int lk[maxn << 1], b[maxn << 1], c[maxn << 1];
bool fg[maxn << 1];
void addedge(int u, int v, int w) {
lk[++ tot] = st[u]; b[tot] = v; c[tot] = w; fg[tot] = 0; st[u] = tot;
}
struct Node {
int u, S;
LD f; LL g, w;
bool operator <(const Node& B) const {
return g < B.g;
}
} A[maxn * (1<<16)]; int AN;
int e[maxn], eN;
void dfsA1(int u, int fa) {
for (int i = st[u]; i; i = lk[i]) {
int v = b[i];
if (v == fa) continue;
e[eN ++] = i;
dfsA1(v, u);
}
}
LL f[maxn][2];
void dfsA2(int u, int fa) {
f[u][0] = 0, f[u][1] = -INF;
for (int i = st[u]; i; i = lk[i]) {
int v = b[i];
if (v == fa) continue;
dfsA2(v, u);
LL f0 = -INF, f1 = -INF;
f0 = f[u][0] + max(f[v][0], f[v][1]);
f1 = f[u][1] + max(f[v][0], f[v][1]);
if (fg[i]) f1 = max(f1, f[u][0] + c[i] + f[v][0]);
f[u][0] = max(f[u][0], f0), f[u][1] = max(f[u][1], f1);
}
}
LD mu[maxn];
bool calc(LD lam) {
AN = 0;
for (int i = 1; i <= n; ++ i) mu[i] = 0;
for (int i = st[rt]; i; i = lk[i]) {
int u = b[i], w = c[i];
eN = 0;
dfsA1(u, rt);
for (int S = 0; S < (1<<eN); ++ S) {
LL sEw = 0;
for (int i = 0; i < eN; ++ i) if (S&(1<<i)) { fg[e[i]] = fg[e[i]^1] = 1; sEw += c[e[i]]; }
dfsA2(u, rt);
for (int i = 0; i < eN; ++ i) if (S&(1<<i)) fg[e[i]] = fg[e[i]^1] = 0;
LL hu = max(f[u][0], f[u][1]);
LD fu = hu - lam * sEw;
LL gu = max(f[u][0] + w, f[u][1]) - hu;
A[AN ++] = (Node){u, S, fu, gu, w};
if (S > 0 && fu < eps)
return 1;
}
}
sort(A, A+AN);
for (int i = 0; i < AN; ++ i) {
int u = A[i].u; LD fu = A[i].f; LL gu = A[i].g;
if (A[i].S == 19) {
int pp = 0;
}
LD s = 0;
for (int j = 1; j <= n; ++ j) if (j != u) s += mu[j];
if (fu + gu - A[i].w * lam + s < eps) return 1;
mu[u] = min(mu[u], fu - A[i].w * lam);
}
return 0;
}
int cen, vcen;
int sz[maxn], mxChSz[maxn];
void dfsC(int u, int fa, int n) {
sz[u] = 1; mxChSz[u] = 0;
for (int i = st[u]; i; i = lk[i]) {
int v = b[i];
if (v == fa) continue;
dfsC(v, u, n);
sz[u] += sz[v];
mxChSz[u] = max(mxChSz[u], sz[v]);
}
mxChSz[u] = max(n - sz[u], mxChSz[u]);
if (mxChSz[u] < vcen) {
cen = u; vcen = mxChSz[u];
}
}
int getCen() {
vcen = n+1;
dfsC(1, 0, n);
return cen;
}
void solve() {
scanf("%d", &n);
tot = 1; memset(st, 0, sizeof st);
for (int i = 1; i < n; ++ i) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w); addedge(v, u, w);
// printf("%d %d %d\n", u, v, w);
}
rt = getCen();
LD L = 0, R = 1 + eps;
for (int z = 20; z --; ) {
LD M = (L + R) / 2;
if (calc(M)) R = M; else L = M;
}
printf("%.8lf\n", (double)R);
}
int main() {
// freopen("J.in", "r", stdin);
// freopen("J.out", "w", stdout);
int kase, i = 0; scanf("%d", &kase);
for (int i = 1; i <= kase; ++ i) {
printf("Case #%d: ", i);
solve();
}
// for(;;);
return 0;
}