CF786E ALT
给一颗 \(n\) 个节点的树,每个边上有一个守卫。有 \(m\) 个居民,每个居民有一个散步路径(两个节点的树上最短路)。一个居民高兴当且仅当他获得了一个宠物或者他散步的路径上所有的守卫都有宠物。宠物可以分配给居民或者守卫者。求最少需要几只宠物才能让所有居民高兴。输出方案。
\(n,m \leq 20000\)
首先建图很显然,源点连居民流量为 \(1\) ,割掉就相当于选居民,守卫连汇点流量为 \(1\) ,割掉相当于选守卫,然后居民向路径上得每个守卫连正无穷的边,求最小割就可以了。
这样子边数是 \(n^2\) ,用树剖优化建图就是 \(n\log^2n\) 的了。
然后输出方案,最小割求方案不是很套路吗,就是这道题,别的题解解释的太麻烦了。
对残量网络中未满流的边缩点,那么我们可以得到最小割的必须边和可行边。
可行边是边的两个端点不在同一个 SCC 中。
必须边是边的两个端点一个和源点在同一个 SCC ,另一个和汇点在同一个 SCC 。
然后方案就出来了,选取所有必须边然后选可行边知道最大流就可以了。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
const int N = 2e4;
const int inf = 1e9;
using namespace std;
struct edges
{
int to,id;
}edge[N * 2 + 5];
int n,m,nxt[N * 2 + 5],head[N + 5],edge_cnt,size[N + 5],top[N + 5],son[N + 5],dep[N + 5],fa[N + 5],dfn[N + 5],dfc,S,T,idc,ID[N + 5],val[N + 5],bk[N * 20 + 5],id[N * 20 + 5];
vector <int> a1,a2;
namespace F
{
const int N = 3e6;
const long long inf = 2e18;
struct edges
{
int to;
long long cost;
}edge[N * 2 + 5];
int nxt[N * 2 + 5],head[N + 5],edge_cnt = 1,dis[N + 5],q[N + 5],cur[N + 5],S,T,dfn[N + 5],dfc,low[N + 5],stk[N + 5],top,co[N + 5],cnt,Vis[N + 5];
void add_edge(int u,int v,long long w)
{
edge[++edge_cnt] = (edges){v,w};
nxt[edge_cnt] = head[u];
head[u] = edge_cnt;
}
void add(int u,int v,long long w)
{
add_edge(u,v,w);
add_edge(v,u,0);
}
int bfs()
{
for (int i = 1;i <= idc;i++)
cur[i] = head[i],dis[i] = 0;
int l = 1,r = 0;
dis[S] = 1;
q[++r] = S;
while (l <= r)
{
int u = q[l++];
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (w && !dis[v])
{
dis[v] = dis[u] + 1;
q[++r] = v;
}
}
}
return dis[T];
}
long long dfs(int u,long long flow)
{
if (u == T)
return flow;
long long sm = 0;
for (int &i = cur[u];i;i = nxt[i])
{
int v = edge[i].to;
long long w = edge[i].cost;
if (dis[v] == dis[u] + 1 && w)
{
long long res = dfs(v,min(w,flow));
edge[i].cost -= res;
edge[i ^ 1].cost += res;
sm += res;
flow -= res;
if (!flow)
break;
}
}
return sm;
}
long long dinic(int s,int t)
{
S = s;T = t;
long long ans = 0;
while (bfs())
ans += dfs(S,inf);
return ans;
}
void clear()
{
edge_cnt = 1;
for (int i = 1;i <= idc;i++)
head[i] = 0;
idc = 0;
}
void tarjan(int u)
{
low[u] = dfn[u] = ++dfc;
stk[++top] = u;
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (!w)
continue;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
}
else
if (!co[v])
low[u] = min(low[u],dfn[v]);
}
if (low[u] == dfn[u])
{
co[u] = ++cnt;
while (stk[top] != u)
co[stk[top--]] = cnt;
top--;
}
}
void solve(int s,int t,int k)
{
for (int i = 1;i <= idc;i++)
if (!dfn[i])
tarjan(i);
for (int i = head[s];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (!w && co[v] == co[t])
a1.push_back(id[v]),k--,Vis[v] = 1;
}
for (int i = head[t];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (w && co[v] == co[s])
a2.push_back(bk[v]),k--,Vis[v] = 1;
}
if (k)
{
for (int i = head[s];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (!w && co[v] != co[s] && !Vis[v] && k)
a1.push_back(id[v]),k--;
}
for (int i = head[t];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (w && co[v] != co[t] && k)
a2.push_back(bk[v]),k--;
}
}
}
}
struct Seg
{
int id[N * 4 + 5];
#define zrt k << 1
#define yrt k << 1 | 1
void build(int k,int l,int r)
{
id[k] = ++idc;
if (l == r)
{
F::add(id[k],T,1);
bk[id[k]] = val[l];
return;
}
int mid = l + r >> 1;
build(zrt,l,mid);
build(yrt,mid + 1,r);
F::add(id[k],id[zrt],inf);
F::add(id[k],id[yrt],inf);
}
void update(int k,int l,int r,int x,int y,int z)
{
if (x > y)
return;
if (l >= x && r <= y)
{
F::add(z,id[k],inf);
return;
}
int mid = l + r >> 1;
if (x <= mid)
update(zrt,l,mid,x,y,z);
if (y > mid)
update(yrt,mid + 1,r,x,y,z);
}
}tree;
void add_edge(int u,int v,int id)
{
edge[++edge_cnt] = (edges){v,id};
nxt[edge_cnt] = head[u];
head[u] = edge_cnt;
}
void dfs1(int u,int f)
{
fa[u] = f;
dep[u] = dep[f] + 1;
size[u] = 1;
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to,id = edge[i].id;
if (v == f)
continue;
ID[v] = id;
dfs1(v,u);
size[u] += size[v];
if (size[v] > size[son[u]])
son[u] = v;
}
}
void dfs2(int u,int to)
{
top[u] = to;
dfn[u] = ++dfc;
val[dfc] = ID[u];
if (son[u])
dfs2(son[u],to);
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to;
if (v == fa[u] || v == son[u])
continue;
dfs2(v,v);
}
}
void update(int x,int y,int z)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x,y);
tree.update(1,1,n,dfn[top[x]],dfn[x],z);
x = fa[top[x]];
}
if (dfn[x] > dfn[y])
swap(x,y);
tree.update(1,1,n,dfn[x] + 1,dfn[y],z);
}
int main()
{
scanf("%d%d",&n,&m);
int u,v;
for (int i = 1;i < n;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v,i);
add_edge(v,u,i);
}
dfs1(1,0);
dfs2(1,1);
S = ++idc;T = ++idc;
tree.build(1,1,n);
for (int i = 1;i <= m;i++)
{
scanf("%d%d",&u,&v);
idc++;
id[idc] = i;
F::add(S,idc,1);
update(u,v,idc);
}
int x = F::dinic(S,T);
cout<<x<<endl;
F::solve(S,T,x);
cout<<a1.size()<<" ";
for (int i = 0;i < a1.size();i++)
printf("%d ",a1[i]);
cout<<endl<<a2.size()<<" ";
for (int i = 0;i < a2.size();i++)
printf("%d ",a2[i]);
return 0;
}