- 题意:\(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;
}