• 题意:\(n\)个点的一棵树上,有\(m\)条路径,第\(i\)条路径上要么有边被覆盖,否则就要\(ans+1\).问最小覆盖的边数及其方案。
  • 思路:
    首先我思维僵化怎么都想不到网络流,在同学的提醒下才明白这是道\(最小割\)的题目。
    然后建图,分为两类:路径编号
    源点向路径编号连边权为\(1\)的边,每条边向汇点连边权为\(1\)的边。(表示两种选法)
    路径向对应路径上的边连\(inf\)的边。
    ps.最小割通常两种用法:1.将点集分为两类并满足最值;2.分割s,t连通性
    然后考虑倍增优化建图!
    每个点记\(b[i][j]\)表示压缩i祖先往上的j个点(包括i)
    有点类似线段树优化建边,用\(b[i][j]\)分别向\(b[i][j-1]\)\(b[f[i][j-1]][j-1]\)连边
    然后底层向所有本身的边编号连边
    易想到,一条路径求到lca拆成两条树上直链分为约\(log_2\)个连刚才被倍增压缩的点。
    然后跑ISAP结束了!
    ?没有!方案怎么求。

(捞~)我因为能求最小割不会求方案鸽了一个小时,去干别的……
再者,我也被某些及其复杂的求边集的题解吓怕了(不知道作者是怎么想的qwq)
后来在luogu发了帖子,5分钟就有人给了我一种非常简单的求法

在残图上从s开始dfs,len=0的边就断开不走,最后一条边如果两端不在同一个集合里面就是方案啦。
上黛玛:

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
const int M=21;
const int X=N*M;
typedef long long ll;
bool vis[X],flag[X];
int id[N],Id[N],gap[X],dis[X],s,t,len[X],n,m,nxt[X],head[X],to[X],ecnt=1,b[N][M],f[N][M],dep[X],Nxt[N],Head[N],To[N],Ecnt,num,inf=0x3f3f3f3f;
void Add_edge(int u,int v,int k) {Nxt[++Ecnt]=Head[u];To[Ecnt]=v;Id[Ecnt]=k;Head[u]=Ecnt;}
void add_edge(int u,int v,int w) {
	nxt[++ecnt]=head[u];to[ecnt]=v;len[ecnt]=w;head[u]=ecnt,flag[ecnt]=1;
	nxt[++ecnt]=head[v];to[ecnt]=u;len[ecnt]=0;head[v]=ecnt;
}
void dfs1(int u) {
	for(int i=Head[u];i;i=Nxt[i]) {
		int v=To[i];
		if(v==f[u][0])continue;
		id[v]=Id[i],f[v][0]=u,b[v][0]=++num,dep[v]=dep[u]+1;
		add_edge(b[v][0],v,inf);
//		printf("%d %d\n",b[v][0],u);
		for(int j=1;(1<<j)<=dep[v];j++) {
			f[v][j]=f[f[v][j-1]][j-1],b[v][j]=++num;
			add_edge(b[v][j],b[v][j-1],inf),add_edge(b[v][j],b[f[v][j-1]][j-1],inf);
//			printf("(%d,%d)%d %d %d\n",v,j,b[v][j],b[v][j-1],b[f[v][j-1]][j-1]);
		}
		dfs1(v);
	}
}
int Lca(int u,int v) {
	if(dep[u]<dep[v]) swap(u,v);
	int k=dep[u]-dep[v];
	for(int i=0;i<=20;i++) if((1<<i)&k) u=f[u][i];
	if(u==v) return u;
	for(int i=20;i>=0;i--) {
		if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
	}
	return f[u][0];
}
void g_up(int u,int l,int x) {
	int k=dep[u]-dep[l];
	for(int i=0;i<=20;i++) if((1<<i)&k)add_edge(x,b[u][i],inf),u=f[u][i];
}
void Build() {
	for(int i=1;i<=m;i++) {
		add_edge(s,i+num,1);
		int x,y;
		scanf("%d%d",&x,&y);
		int l=Lca(x,y);
		g_up(x,l,i+num),g_up(y,l,i+num);
	}
	for(int i=2;i<=n;i++) add_edge(i,t,1);
}
queue<int> Q;
void BFS() {
	memset(dis,-1,sizeof(dis));
	Q.push(t); dis[t]=0,gap[0]++;
	while(!Q.empty()) {
		int u=Q.front(); Q.pop();
//		printf("%d %d\n",u,dis[u]);
		for(int i=head[u];i;i=nxt[i]) {
			int v=to[i];
			if(dis[v]==-1) dis[v]=dis[u]+1,gap[dis[v]]++,Q.push(v);
		}
	}
}
ll dfs(int u,ll flow) {
	if(u==t) return flow;
	ll used=0;
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];
		if(len[i]&&dis[v]+1==dis[u]) {
			ll tmp=dfs(v,min(1ll*len[i],flow-used));
			if(tmp) {
				len[i]-=tmp,len[i^1]+=tmp;
				used+=tmp;
				if(used==flow) return flow;
			}
		}
	}
	if(!(--gap[dis[u]])) dis[s]=t+1;
	gap[++dis[u]]++;
	return used;
}
ll ISAP() {
	ll maxflow=0;
	for(BFS();dis[s]<t+1;maxflow+=dfs(s,inf));
	return maxflow;
}
void fd(int u) {
	vis[u]=1;
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];
		if(!vis[v]&&len[i]) fd(v);
	}
}
int p1[N],p2[N],c1,c2;
int main() {
//	freopen("ll.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		Add_edge(u,v,i),Add_edge(v,u,i);
	}
	num=n;
	dfs1(1);
	s=0,t=num+m+1;
	Build();
	ll ans=ISAP();
	printf("%lld\n",ans);
	fd(s);
	
	for(int i=s;i<=t;i++) {
		for(int j=head[i];j;j=nxt[j]) {
			int v=to[j];
//			printf("%d %d %d\n",i,v,len[j]);
			if(flag[j]&&vis[i]&&!vis[v]) {
				if(i==s) p1[++c1]=v-num;
				else p2[++c2]=id[i];
			}
		}
	}
	printf("%d ",c1);
	for(int i=1;i<=c1;i++) printf("%d ",p1[i]);
	puts("");
	printf("%d ",c2);
	for(int i=1;i<=c2;i++) printf("%d ",p2[i]);
	return 0;
}