链接:
题意:
给一个图和两棵树,你要让第一棵树是最小生成树,第二棵树是最大生成树,改变的边权之和最小。
题解:
网络流。
首先可以很容易地转化为一个一般性问题:
记新边权为
w
w
,旧边权为,有一些形如限制
wi≤wj
w
i
≤
w
j
的限制,使
∑|wi−vi|
∑
|
w
i
−
v
i
|
最小。
记
fi(x)=|x−vi|
f
i
(
x
)
=
|
x
−
v
i
|
,
f′i(x)=fi(x)−fi(x−1)
f
i
′
(
x
)
=
f
i
(
x
)
−
f
i
(
x
−
1
)
,注意到
f′i(x)
f
i
′
(
x
)
是不降的。
考虑对于一个给定的
x
x
,如何求出在最优解中哪些边的边权不超过。
将每个边变成网络流上的一个点,点权为
f′i(x)
f
i
′
(
x
)
,同时如果
wi≤wj
w
i
≤
w
j
,就从
i
i
向连一条边。
求出一个最小权闭合子图记为
C
C
,那么中的点最终的
w
w
会大于等于。
下面给出证明:
假设存在
D⊂C
D
⊂
C
,同时
D
D
中的点在最优解中小于,注意到一定不会有
C−D
C
−
D
到
D
D
的边,这意味着也是一个闭合子图。
由于
C
C
是最小权闭合子图,所以,由于
f′i(x)
f
i
′
(
x
)
不降,所以将
D
D
的权值改为不会劣。
最后实际上是
logmax(vi)
l
o
g
m
a
x
(
v
i
)
次网络流的复杂度。
代码:
#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define mset(x, y) memset(x, y, sizeof x)
#define mcpy(x, y) memcpy(x, y, sizeof x)
using namespace std;
typedef long long LL;
typedef pair <int, int> pii;
const int MAXN = 1005;
const int MAXM = 200005;
const int INF = 0x3f3f3f3f;
bool edg[MAXN][MAXN], tag[MAXN], chosen[MAXN];
int n, m, ql, qr, q[MAXN], dep[MAXN], w[MAXN];
vector <pii> adj[MAXN];
pii par[MAXN];
LL ans;
inline void Dfs(int x)
{
for (auto e : adj[x])
if (e.xx ^ par[x].xx)
par[e.xx] = mp(x, e.yy), dep[e.xx] = dep[x] + 1, Dfs(e.xx);
}
inline void Add(int x, int y, int i, bool t)
{
if (x == y)
return ;
if (dep[x] < dep[y])
{
if (!t)
edg[par[y].yy][i] = 1;
else
edg[i][par[y].yy] = 1;
Add(x, par[y].xx, i, t);
}
else
{
if (!t)
edg[par[x].yy][i] = 1;
else
edg[i][par[x].yy] = 1;
Add(par[x].xx, y, i, t);
}
}
namespace Flow
{
struct Edge
{
int p, v, w;
} e[MAXM << 3];
int e_cnt, S, T, V, ql, qr, q[MAXN], hed[MAXN], dis[MAXN], cur[MAXN];
inline void Init(int n)
{
V = n;
for (int i = 0; i < V; i ++)
hed[i] = 0;
e_cnt = 1;
}
inline void Addedge(int x, int y, int w)
{
e[++ e_cnt] = {y, hed[x], w}; hed[x] = e_cnt;
e[++ e_cnt] = {x, hed[y], 0}; hed[y] = e_cnt;
}
inline bool Bfs()
{
for (int i = 0; i < V; i ++)
dis[i] = 0;
dis[q[ql = 0] = S] = qr = 1;
while (ql ^ qr)
{
int x = q[ql ++];
for (int i = hed[x]; i; i = e[i].v)
if (e[i].w && !dis[e[i].p])
dis[q[qr ++] = e[i].p] = dis[x] + 1;
}
return dis[T];
}
inline int Dfs(int x, int f)
{
if (x == T)
return f;
int ret = 0, t = 0;
for (int &i = cur[x]; i; i = e[i].v)
if (e[i].w && dis[e[i].p] == dis[x] + 1)
{
t = Dfs(e[i].p, min(f, e[i].w));
f -= t; ret += t; e[i].w -= t; e[i ^ 1].w += t;
if (!f)
return ret;
}
return dis[x] = -1, ret;
}
inline int Dinic()
{
int ret = 0;
while (Bfs())
{
for (int i = 0; i < V; i ++)
cur[i] = hed[i];
ret += Dfs(S, INF);
}
return ret;
}
}
inline void Solve(int l, int r, vector <int> &cur)
{
if (cur.size() == 0)
return ;
if (cur.size() == 1)
{
if (w[cur[0]] < l)
ans += l - w[cur[0]];
if (w[cur[0]] > r)
ans += w[cur[0]] - r;
return ;
}
if (l == r)
{
for (auto x : cur)
ans += abs(w[x] - l);
return ;
}
int n = cur.size(), mid = l + r >> 1;
Flow::Init(n + 2);
Flow::S = n, Flow::T = n + 1;
for (int i = 0; i < cur.size(); i ++)
if (w[cur[i]] > mid)
Flow::Addedge(Flow::S, i, 1);
else
Flow::Addedge(i, Flow::T, 1);
for (int i = 0; i < cur.size(); i ++)
for (int j = 0; j < cur.size(); j ++)
if (edg[cur[i]][cur[j]])
Flow::Addedge(i, j, INF);
Flow::Dinic();
for (int i = 0; i < n; i ++)
chosen[i] = Flow::dis[i];
vector <int> p, q;
for (int i = 0; i < n; i ++)
if (chosen[i])
p.pb(cur[i]);
else
q.pb(cur[i]);
Solve(l, mid, q);
Solve(mid + 1, r, p);
}
class ExtremeSpanningTrees
{
public:
LL minTime(vector <int> a, vector <int> b, vector <int> w, vector <int> m1, vector <int> m2)
{
n = m1.size() + 1, m = a.size(), ans = 0;
mset(edg, 0);
for (int i = 0; i < m; i ++)
::w[i] = w[i];
for (int i = 0; i < n; i ++)
adj[i].clear();
for (int i = 0; i < m; i ++)
tag[i] = 0;
for (int i = 0, u, v; i < n - 1; i ++)
u = a[m1[i]], v = b[m1[i]], adj[u].pb(mp(v, m1[i])), adj[v].pb(mp(u, m1[i])), tag[m1[i]] = 1;
par[0] = mp(-1, -1);
Dfs(0);
for (int i = 0; i < m; i ++)
if (!tag[i])
Add(a[i], b[i], i, 0);
for (int i = 0; i < n; i ++)
adj[i].clear();
for (int i = 0; i < m; i ++)
tag[i] = 0;
for (int i = 0, u, v; i < n - 1; i ++)
u = a[m2[i]], v = b[m2[i]], adj[u].pb(mp(v, m2[i])), adj[v].pb(mp(u, m2[i])), tag[m2[i]] = 1;
par[0] = mp(-1, -1);
Dfs(0);
for (int i = 0; i < m; i ++)
if (!tag[i])
Add(a[i], b[i], i, 1);
vector <int> cur;
int l = w[0], r = w[0];
for (int i = 0; i < m; i ++)
cur.pb(i), l = min(l, w[i]), r = max(r, w[i]);
Solve(l, r, cur);
return ans;
}
};