【BZOJ2893】征服王
题目
题解
第一步自然是缩点变成无向图。
然后就是裸的可相交最小路径覆盖。
不可相交最小路径覆盖
即用最少的路径覆盖图中所有点, 路径不可相交。
可用最小割解决。
考虑把原图中的每一个点看成一条单独的路径, 接着不断将路径合并, 形成更长的路径。
那么最后的路径数 = 总点数 - 合并次数\((\)没合并次减少一条路径\()\), 要使路径数最小就要让合并次数最多。
可用二分图匹配计算。
把原图中的一个点\(u\), 拆成\(u_1\)和\(u_2\);把原图中的边\((u, v)\), 改成\((u_1, v_2)\)。 那么就构成了一个二分图。在二分图中加一条匹配边等于一次合并, 最大匹配即为最大合并次数。
可相交最小路径覆盖
做法一:
即用最少的路径覆盖图中所有点, 路径可相交。
考虑把在上一个问题的算法直接搬过来。 显然是错的, 因为如果两个点的路径中的任意一个点有被覆盖过, 则两个点就会被无法被合并。
我们要消除这种影响。 换句话说, 只要\(A\)可到达\(B\), 那么在二分图就要有一条\((A_1, B_2)\)的边。
然后在求二分图匹配, 即可得到正确的合并次数。
做法二:
最大费用最大流摸拟贪心。
代码
做法一:
#include <iostream>
#include <cstdlib>
#include <stack>
#include <cstdio>
#include <cstring>
#include <bitset>
#include <queue>
using namespace std;
const int N = 1210, M = 200010;
const int INF = 0x3F3F3F3F;
struct edge
{ int from, to, flow, cap;
edge() { }
edge(int _1, int _2, int _3, int _4) : from(_1), to(_2), flow(_3), cap(_4) { }
};
namespace Graph
{ edge edges[M];
int head[N], nxt[M], tot;
inline void init()
{ memset(head, -1, sizeof(head));
tot = 0;
}
inline void add_edge(int x, int y)
{ edges[tot] = edge(x, y, 0, 0);
nxt[tot] = head[x];
head[x] = tot++;
}
}
namespace DAG
{ edge edges[M];
int head[N], nxt[M], tot;
inline void init()
{ memset(head, -1, sizeof(head));
tot = 0;
}
inline void add_edge(int x, int y)
{ edges[tot] = edge(x, y, 0, 0);
nxt[tot] = head[x];
head[x] = tot++;
}
}
struct Dinic
{ edge edges[N * N * 2];
int head[2 * N], nxt[N * N * 2], tot;
inline void init()
{ memset(head, -1, sizeof(head));
tot = 0;
}
inline void add_edge(int x, int y, int z)
{ edges[tot] = edge(x, y, 0, z);
nxt[tot] = head[x];
head[x] = tot++;
edges[tot] = edge(y, x, 0, 0);
nxt[tot] = head[y];
head[y] = tot++;
}
int L, R;
int s, t;
int d[2 * N];
bool bfs()
{ memset(d, -1, sizeof(d));
queue<int> q;
q.push(s);
d[s] = 0;
while (!q.empty())
{ int x = q.front(); q.pop();
for (int i = head[x]; ~i; i = nxt[i])
{ edge & e = edges[i];
if (e.cap > e.flow && d[e.to] == -1)
{ d[e.to] = d[x] + 1;
q.push(e.to);
}
}
}
return d[t] != -1;
}
int cur[2 * N];
int dfs(int x, int a)
{ if (x == t || a == 0) return a;
int f, flow = 0;
for (int & i = cur[x]; ~i; i = nxt[i])
{ edge & e = edges[i];
if (d[e.to] == d[x] + 1 && (f = dfs(e.to, min(e.cap-e.flow, a))) > 0)
{ e.flow += f;
edges[i^1].flow -= f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int maxflow(int _s, int _t)
{ s = _s, t = _t;
int flow = 0;
while (bfs())
{ for (int i = L; i <= R; i++)
cur[i] = head[i];
flow += dfs(s, INF);
}
return flow;
}
} dinic;
int n, m;
bool bo1[N], bo2[N], boo1[N], boo2[N];
int dfn[N], low[N], dfs_clock;
int num[N], scc;
stack<int> st;
bool boo[N], bo[N];
void Tarjan(int x)
{ using namespace Graph;
low[x] = dfn[x] = ++dfs_clock;
st.push(x);
for (int i = head[x]; ~i; i = nxt[i])
{ edge & e = edges[i];
if (!dfn[e.to])
{ Tarjan(e.to);
low[x] = min(low[x], low[e.to]);
}
else if (!num[e.to]) low[x] = min(low[x], dfn[e.to]);
}
if (low[x] == dfn[x])
{ int v;
++scc;
do
{ v = st.top(); st.pop();
num[v] = scc;
if (boo1[v]) bo1[scc] = 1;
if (boo2[v]) bo2[scc] = 1;
}
while(v != x);
}
}
int out[N], in[N];
int Ans;
queue<int> q;
int node[N], total;
bitset<N> f[N];
void solve()
{ using namespace DAG;
for (int i = 1; i <= scc; i++)
if (in[i] == 0 && !bo1[i])
{ puts("no solution");
return;
}
for (int i = 1; i <= scc; i++)
if (out[i] == 0 && !bo2[i])
{ puts("no solution");
return;
}
total = 0;
while (!q.empty()) q.pop();
for (int i = 1; i <= scc; i++)
if (in[i] == 0)
q.push(i), node[++total] = i;
while (!q.empty())
{ int x = q.front(); q.pop();
for (int i = head[x]; ~i; i = nxt[i])
{ edge & e = edges[i];
if ((--in[e.to]) == 0)
q.push(e.to),
node[++total] = e.to;
}
}
int S = 2 * scc + 1, T = 2 * scc + 2;
dinic.L = 0, dinic.R = T;
dinic.init();
for (int i = 1; i <= scc; i++)
dinic.add_edge(S, i, 1);
for (int i = 1; i <= scc; i++)
dinic.add_edge(i+scc, T, 1);
for (int i = total; i >= 1; i--)
{ int now = node[i];
f[now][now-1] = 1;
for (int j = head[now]; ~j; j = nxt[j])
{ edge & e = edges[j];
f[now] |= f[e.to];
}
for (int j = 1; j <= scc; j++)
if (f[now][j-1] && j != now)
dinic.add_edge(now, j+scc, 1);
}
for (int i = 1; i <= n; i++)
f[i].reset();
printf("%d\n", scc - dinic.maxflow(S, T));
}
void build_DAG()
{ DAG::init();
for (int i = 1; i <= n; i++)
{ for (int j = Graph::head[i]; ~j; j = Graph::nxt[j])
{ edge & e = Graph::edges[j];
if (num[i] != num[e.to])
DAG::add_edge(num[i], num[e.to]),
in[num[e.to]]++,
out[num[i]]++;
}
}
}
void clear()
{ memset(boo1, 0, sizeof(boo1));
memset(boo2, 0, sizeof(boo2));
memset(bo1, 0, sizeof(bo1));
memset(bo2, 0, sizeof(bo2));
memset(num, 0, sizeof(num));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
dfs_clock = 0;
scc = 0;
while (!st.empty()) st.pop();
}
int main()
{ int T;
scanf("%d", &T);
while (T--)
{ int a, b;
scanf("%d %d %d %d", &n, &m, &a, &b);
clear();
for (int i = 1; i <= a; i++)
{ int x;
scanf("%d", &x);
boo1[x] = 1;
}
for (int i = 1; i <= b; i++)
{ int x;
scanf("%d", &x);
boo2[x] = 1;
}
Ans = 0;
Graph::init();
for (int i = 1; i <= m; i++)
{ int x, y;
scanf("%d %d", &x, &y);
Graph::add_edge(x, y);
}
for (int i = 1; i <= n; i++)
if (!dfn[i]) Tarjan(i);
build_DAG();
solve();
}
return 0;
}
做法二:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
const int N = 1010, M = 10010;
const int INF = 0x3F3F3F3F;
struct edge
{
int from, to, flow, cap, dis;
edge() { }
edge(int _1, int _2, int _3, int _4, int _5) : from(_1), to(_2), flow(_3), cap(_4), dis(_5) { }
};
namespace Graph
{
edge edges[2*M];
int head[N], nxt[2*M], tot;
inline void init()
{
memset(head, -1, sizeof(head));
tot = 0;
}
inline void add_edge(int x, int y)
{
edges[tot] = edge(x, y, 0, 0, 0);
nxt[tot] = head[x];
head[x] = tot++;
}
}
struct MCMF
{
edge edges[2 * N * N];
int head[2*N], nxt[2 * N * N], tot;
inline void init()
{
memset(head, -1, sizeof(head));
tot = 0;
}
inline void add_edge(int x, int y, int z, int k)
{
edges[tot] = edge(x, y, 0, z, k);
nxt[tot] = head[x];
head[x] = tot++;
edges[tot] = edge(y, x, 0, 0, -k);
nxt[tot] = head[y];
head[y] = tot++;
}
int s, t;
int L, R;
int dist[2*N]; bool inq[2*N];
int a[2*N], p[2*N];
bool SPFA()
{
for (int i = L; i <= R; i++)
dist[i] = -INF, inq[i] = 0;
queue <int> q;
q.push(s);
inq[s] = 1;
dist[s] = 0;
a[s] = INF;
p[s] = 0;
while (!q.empty())
{
int x = q.front(); q.pop();
inq[x] = 0;
for (int i = head[x]; ~i; i = nxt[i])
{
edge & e = edges[i];
if (e.cap > e.flow && dist[e.to] < dist[x] + e.dis)
{
dist[e.to] = dist[x] + e.dis;
a[e.to] = min(a[x], e.cap - e.flow);
p[e.to] = i;
if (!inq[e.to]) q.push(e.to), inq[e.to] = 1;
}
}
}
if (dist[t] <= 0) return 0;
for (int i = t; i != s; i = edges[p[i]].from)
{
edges[p[i]].flow += a[t];
edges[p[i]^1].flow -= a[t];
}
return 1;
}
int calc(int _s, int _t)
{
int Ans = 0;
s = _s, t = _t;
while (SPFA()) Ans++;
return Ans;
}
} maxcostminflow;
int n, m, a, b;
bool type1[N], type2[N];
bool bo1[N], bo2[N];
int low[N], dfn[N], dfs_clock;
int bel[N], cnt;
stack <int> stk;
void Tarjan(int x)
{
using namespace Graph;
low[x] = dfn[x] = ++dfs_clock;
stk.push(x);
for (int i = head[x]; ~i; i = nxt[i])
{
edge & e = edges[i];
if (!dfn[e.to])
{
Tarjan(e.to);
low[x] = min(low[x], low[e.to]);
}
else if (!bel[e.to]) low[x] = min(low[x], dfn[e.to]);
}
if (low[x] == dfn[x])
{
int v = 0;
cnt++;
do
{
v = stk.top(); stk.pop();
bel[v] = cnt;
if (type1[v]) bo1[cnt] = 1;
if (type2[v]) bo2[cnt] = 1;
}
while (v != x);
}
}
bool g[N][N];
int deg1[N], deg2[N];
void solve()
{
memset(dfn, 0, sizeof(dfn));
memset(bel, 0, sizeof(bel));
memset(g, 0, sizeof(g));
memset(bo1, 0, sizeof(bo1));
memset(bo2, 0, sizeof(bo2));
cnt = 0;
dfs_clock = 0;
for (int i = 1; i <= n; i++)
if (!dfn[i]) Tarjan(i);
for (int i = 0; i < Graph::tot; i++)
{
edge & e = Graph::edges[i];
if (bel[e.from] != bel[e.to]) g[bel[e.from]][bel[e.to]] = 1;
}
memset(deg1, 0, sizeof(deg1));
memset(deg2, 0, sizeof(deg2));
for (int i = 1; i <= cnt; i++)
for (int j = 1; j <= cnt; j++)
if (i != j && g[i][j])
deg2[i]++, deg1[j]++;
for (int i = 1; i <= cnt; i++)
if (!deg1[i] && !bo1[i]) return void(puts("no solution"));
for (int i = 1; i <= cnt; i++)
if (!deg2[i] && !bo2[i]) return void(puts("no solution"));
maxcostminflow.init();
maxcostminflow.L = 0, maxcostminflow.R = 2 * cnt + 2;
int S = cnt + cnt + 1, T = cnt + cnt + 2;
for (int i = 1; i <= cnt; i++)
maxcostminflow.add_edge(S, i, INF, 0);
for (int i = 1; i <= cnt; i++)
maxcostminflow.add_edge(i+cnt, T, INF, 0);
for (int i = 1; i <= cnt; i++)
{
maxcostminflow.add_edge(i, i+cnt, 1, 1);
maxcostminflow.add_edge(i, i+cnt, INF, 0);
}
for (int i = 1; i <= cnt; i++)
for (int j = 1; j <= n; j++)
if (g[i][j])
maxcostminflow.add_edge(i+cnt, j, INF, 0);
printf("%d\n", maxcostminflow.calc(S, T));
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
memset(type1, 0, sizeof(type1));
memset(type2, 0, sizeof(type2));
scanf("%d %d %d %d", &n, &m, &a, &b);
Graph::init();
for (int i = 1; i <= a; i++)
{
int x;
scanf("%d", &x);
type1[x] = 1;
}
for (int i = 1; i <= b; i++)
{
int x;
scanf("%d", &x);
type2[x] = 1;
}
for (int i = 1; i <= m; i++)
{
int x, y;
scanf("%d %d", &x, &y);
Graph::add_edge(x, y);
}
solve();
}
return 0;
}