题目大意
给一颗n个节点的树,每个边上有一个守卫。有m个居民,每个居民有一个散步路径(两个节点的树上最短路)。一个居民高兴当且仅当他获得了一个宠物或者他散步的路径上所有的守卫都有宠物。求最少需要几个宠物能让所有居民高兴。输出方案。
n,m <= 20000
输入格式:
第一行两个整数,n和m
接下来n-1行,每行两个整数,代表一条边
接下来m行,每行两个整数,代表一个居民的路径端点。
输出格式:
第一行一个数ans,表示最少的宠物数
第二行一个数p,代表给居民发的宠物数,接下来p个整数,表示获得宠物的居民
第三行一个数w,表示给守卫发的宠物数,接下来w个整数,表示获得宠物的守卫(编号为输入顺序)
题解
不难想到一个最小割模型,源点向居民连,守卫向汇点连,居民向覆盖的所有守卫连边,然后最小割。
然后倍增优化连边。
这题主要还得输出方案。
第一次遇到最小割要输出方案的。
方法是这样的,首先我们从源点开始(dfs)有流量的边,那么左部点一定是没有选的,(dfs)到的右部点一定是选了的,这时如果右部点有向左部点连的边,说明这个左部点也没选,那么继续从左部点开始点(dfs)。
这样所有(dfs)到的左部点都没有被选,所有(dfs)到的右部点都被选了。
代码
#include<bits/stdc++.h>
#define inf 2e9
#define N 20009
#define mm make_pair
#define P pair<int,int>
using namespace std;
typedef long long ll;
int p[20][N],id[20][N],now,tot=1,dep[N],n,m,rec[N];
int head[N<<5],cur[N<<5],deep[N<<5];
bool vis[N<<5];
P re[N];
queue<int>q;
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
vector<int>vec[N],ans1,ans2;
vector<int>::iterator it;
struct edge{
int n,to,l;
}e[N<<6];
inline void add(int u,int v,int l){
e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;
e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=0;
}
inline bool bfs(int s,int t){
memset(deep,0,sizeof(deep));
memcpy(cur,head,sizeof(cur));
q.push(s);deep[s]=1;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].n){
int v=e[i].to;
if(!deep[v]&&e[i].l){
deep[v]=deep[u]+1;
q.push(v);
}
}
}
return deep[t];
}
int dfs(int u,int t,int l){
if(u==t||!l)return l;
int f,flow=0;
for(int &i=cur[u];i;i=e[i].n){
int v=e[i].to;
if(deep[u]+1==deep[v]&&(f=dfs(v,t,min(l,e[i].l)))){
flow+=f;e[i].l-=f;e[i^1].l+=f;l-=f;
if(!l)break;
}
}
return flow;
}
void dfs(int u,int fa){
for(int i=1;(1<<i)<=dep[u];++i){
p[i][u]=p[i-1][p[i-1][u]];
id[i][u]=++now;
add(id[i][u],id[i-1][u],inf);
add(id[i][u],id[i-1][p[i-1][u]],inf);
}
for(vector<int>::iterator it=vec[u].begin();it!=vec[u].end();++it){
int v=*it;
if(v==fa)continue;
dep[v]=dep[u]+1;
p[0][v]=u;id[0][v]=++now;
dfs(v,u);
}
}
inline int getlca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
for(int i=19;i>=0;--i)if(dep[u]-(1<<i)>=dep[v])u=p[i][u];
if(u==v)return u;
for(int i=19;i>=0;--i)if(p[i][u]!=p[i][v]){
u=p[i][u];v=p[i][v];
}
return p[0][u];
}
void ser(int u){
vis[u]=1;
for(int i=head[u];i;i=e[i].n){
int v=e[i].to;
if(!vis[v]&&e[i].l)ser(v);
}
}
int main(){
n=rd();m=rd();
int x,y;
for(int i=1;i<n;++i){
x=rd();y=rd();
vec[x].push_back(y);
vec[y].push_back(x);
re[i]=mm(x,y);
}
dfs(1,0);
for(int i=1;i<=m;++i){
x=rd();y=rd();
++now;add(0,now,1);
rec[i]=now;
int lca=getlca(x,y);
for(int j=19;j>=0;--j){
if(p[j][x]&&dep[p[j][x]]>=dep[lca]){
add(now,id[j][x],inf);
x=p[j][x];
}
}
for(int j=19;j>=0;--j){
if(p[j][y]&&dep[p[j][y]]>=dep[lca]){
add(now,id[j][y],inf);
y=p[j][y];
}
}
}
int ans=0;
for(int i=2;i<=n;++i)add(id[0][i],now+1,1);
while(bfs(0,now+1))ans+=dfs(0,now+1,inf);
cout<<ans<<endl;
ser(0);
for(int i=1;i<=m;++i){
if(!vis[rec[i]])ans1.push_back(i);
}
for(int i=1;i<n;++i){
if(dep[re[i].first]<dep[re[i].second])swap(re[i].first,re[i].second);
if(vis[id[0][re[i].first]])ans2.push_back(i);
}
printf("%d ",ans1.size());
for(vector<int>::iterator it=ans1.begin();it!=ans1.end();++it)
printf("%d ",*it);puts("");
printf("%d ",ans2.size());
for(vector<int>::iterator it=ans2.begin();it!=ans2.end();++it)
printf("%d ",*it);
return 0;
}