1 条题解
-
0
自动搬运
来自洛谷,原作者为

LHF
qwq搬运于
2025-08-24 22:05:03,当前版本为作者最后更新于2019-11-14 18:21:41,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
终于AC这一题了,发个题解庆祝一下吧!!!
首先,大家要先用Dinic或ISAP做对【模板】网络最大流(不会网络最大流的可以点这里),根据推断,网络最大流=网络最小割。
首先,我们要知道。按照正常暴力的算法,这一题会被T飞的。为了防止TLE事件
不再发生。我们就要采取相应的优化措施。怎么弄?其实也不难。我们可以建立一棵最小割树。 (最小割树?啥?)
其实,我们可以这样做:

先建立一个无向图,然后再把这个图的整体看做一个集合,然后任意选择两个点(设它们为1和5),在原图上跑一趟最小割。如图:

然后,因为1和5已经无法连接了,所以我们可以把1和5再分成两个集合,中间由一条边连接,这条边的权值为两点之间的最小割。然后,我们再对每个集合进行这样的操作(注意,最小割要在原图上跑),再分下去,直到集合内只剩一个节点为止。如图:




最后,我们可以得到一棵最小割树。
这里要注意,跑最小割(最大流)要在原图上跑!!!
大功告成(〃'▽'〃),这棵树中任意两点的最小割等于原图上任意两点的最小割,因此,我们直接用BFS来查找就行了,查找一个的时间复杂度是O(n),这样求Q个最小割只需要用O(Qn)的时间复杂度,查询代码如下:
int findans() { head=0,tail=1; d[head]=a; for(register int i=0;i<=n;i++) dis[i]=0; dis[a]=0x7ffffff; while(head<tail) { t=d[head]; head++; for(register int i=trf[t];i;i=tr[i].next) { if(dis[trar]==0) { dis[trar]=min(dis[t],tr[i].flow+1); if(trar==b) return dis[b]-1; d[tail]=trar; tail++; } } } return dis[b]-1; }然后就是如何构建这一棵树。其实也不难,因为Dinic或ISAP跑dfs时图中有一些边的容量为0,其实就代表要割掉那一条边,所以我们只需要用搜索的方式来寻找两个集合中分别包含那些数,代码如下:
void build(int l,int r) { if(l>=r)return; //在原图上跑两点之间的最小割 a=v[l]; b=v[l+1]; dinic(); //给两个集合之间加一条无向边 adtr(a,b,ans); adtr(b,a,ans); //寻找两个集合的元素 int tl1=0,tl2=0; for(register int i=l;i<=r;i++) { if(dis[v[i]]) { tl1++; t1[tl1]=v[i]; } else { tl2++; t2[tl2]=v[i]; } } for(register int i=1;i<=tl1;i++) v[i+l-1]=t1[i]; for(register int i=1;i<=tl2;i++) v[l+tl1+i-1]=t2[i]; //分治下去 build(l,tl1+l-1); build(l+tl1,r); }好了,最后来一份完整代码:
#pragma GCC optimize(3) #include<cstdio> #define N 100010 #define kar k[i].ar #define trar tr[i].ar #define min(a,b) (a<b?a:b) using namespace std; int a,b,t,n,m,f[N],ans,trl; struct node { int next,ar,flow; } k[N*20],tr[N*20]; int trf[N],ss; int first[N],dis[N],len; int x[N],y[N],v[N],e[N]; int t1[N],t2[N],tl1,tl2; void add(int a,int b,int t) { len++; k[len].ar=b; k[len].next=first[a]; first[a]=len; k[len].flow=t; } void adtr(int a,int b,int t) { trl++; tr[trl].ar=b; tr[trl].next=trf[a]; trf[a]=trl; tr[trl].flow=t; } int head,tail,d[N]; bool bfs() { head=0,tail=1; d[0]=a; for(register int i=0; i<=n; i++) dis[i]=0; dis[a]=1; while(head<tail) { t=d[head]; head++; for(register int i=first[t]; i; i=k[i].next) { if(dis[kar]==0&&k[i].flow>0) { dis[kar]=dis[t]+1; if(kar==b) return true; d[tail]=kar; tail++; } } } return false; } int dfs(int xx,int flow) { if(xx==b) return flow; if(flow==0) return 0; if(dis[xx]>=dis[b])return 0; int h,s=0; for(register int i=first[xx]; i>1; i=k[i].next) { if(flow==0) { dis[xx]=0; break; } if(dis[kar]==dis[xx]+1&&k[i].flow>0) { h=dfs(kar,min(flow,k[i].flow)); s+=h; flow-=h; k[i].flow-=h; k[i^1].flow+=h; if(h==0) dis[kar]=0; } } return s; } void dinic() { for(register int i=0; i<=n; i++) { dis[i]=0; first[i]=0; } len=1,ans=0; for(register int i=1; i<=m; i++) add(x[i],y[i],f[i]),add(y[i],x[i],f[i]); while(bfs()) ans+=dfs(a,0x7ffffff); } void build(int l,int r) { if(l>=r)return; a=v[l]; b=v[l+1]; dinic(); adtr(a,b,ans); adtr(b,a,ans); int tl1=0,tl2=0; for(register int i=l; i<=r; i++) { if(dis[v[i]]) tl1++,t1[tl1]=v[i]; else tl2++,t2[tl2]=v[i]; } for(register int i=1; i<=tl1; i++) v[i+l-1]=t1[i]; for(register int i=1; i<=tl2; i++) v[l+tl1+i-1]=t2[i]; build(l,tl1+l-1); build(l+tl1,r); } int findans() { head=0,tail=1; d[head]=a; for(register int i=0; i<=n; i++) dis[i]=0; dis[a]=0x7ffffff; while(head<tail) { t=d[head]; head++; for(register int i=trf[t]; i; i=tr[i].next) { if(dis[trar]==0) { dis[trar]=min(dis[t],tr[i].flow+1); if(trar==b) return dis[b]-1; d[tail]=trar; tail++; } } } return dis[b]-1; } int main() { int T; len=1; scanf("%d%d",&n,&m); for(register int i=1; i<=m; i++) { scanf("%d%d%d",&a,&b,&t); x[i]=a,y[i]=b,f[i]=t; } for(register int i=0; i<=n; i++) v[i]=i; build(0,n); scanf("%d",&T); while(T--) { scanf("%d%d",&a,&b); printf("%d\n",findans()); } return 0; }好了,到此结束了(逃)。
- 1
信息
- ID
- 3916
- 时间
- 500ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者