做过ZJOI2016(QAQ)的旅行者之后这道题目就变成一眼出的题了。。。
首先选两个点跑最小割更新答案,然后把和S连在一起的递归下去,和T连在一起的递归下去就好了。。但是不会证明。。。。。
AC代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 205
#define M 6005
using namespace std;
int n,m,tot,sta,gol,fst[N],cur[N],pnt[M],len[M],nxt[M],d[N],h[N],num[N],fa[N],a[N],b[N],c[N][N];
bool vis[N];
void add(int x,int y,int z){
pnt[++tot]=y; len[tot]=z; nxt[tot]=fst[x]; fst[x]=tot;
}
void bfs(){
memset(num,0,sizeof(num));
memset(d,0x3f,sizeof(d)); d[gol]=0;
int head=0,tail=1; h[1]=gol;
while (head<tail){
int x=h[++head],p; num[d[x]]++;
for (p=fst[x]; p; p=nxt[p]) if (len[p^1]){
int y=pnt[p];
if (d[x]+1<d[y]){
d[y]=d[x]+1; h[++tail]=y;
}
}
}
}
int up(){
int i,tmp=1000000000;
for (i=gol; i!=sta; i=pnt[fa[i]^1]) tmp=min(tmp,len[fa[i]]);
for (i=gol; i!=sta; i=pnt[fa[i]^1]){
len[fa[i]]-=tmp; len[fa[i]^1]+=tmp;
}
return tmp;
}
int isap(){
int i;
for (i=2; i<=tot; i++){ len[i]=len[i^1]=(len[i]+len[i^1])>>1; }
bfs(); memcpy(cur,fst,sizeof(fst));
int x=sta,flow=0,p; bool flag;
while (d[x]<n){
if (x==gol){ flow+=up(); x=sta; }
flag=1;
for (p=cur[x]; p; p=nxt[p]) if (len[p]){
int y=pnt[p];
if (d[y]+1==d[x]){
fa[y]=cur[x]=p; x=y;
flag=0; break;
}
}
if (flag){
int mn=n-1; cur[x]=fst[x];
for (p=fst[x]; p; p=nxt[p])
if (len[p]) mn=min(mn,d[pnt[p]]);
num[d[x]]--; if (!num[d[x]]) break;
d[x]=mn+1; num[d[x]]++;
if (x!=sta) x=pnt[fa[x]^1];
}
}
return flow;
}
void dfs(int x){
vis[x]=1; int p;
for (p=fst[x]; p; p=nxt[p]) if (len[p] && !vis[pnt[p]]) dfs(pnt[p]);
}
void solve(int l,int r){
if (l==r) return;
sta=a[l]; gol=a[r]; int flow=isap(),i,j;
memset(vis,0,sizeof(vis)); dfs(sta);
for (i=1; i<=n; i++) if (vis[i])
for (j=1; j<=n; j++) if (!vis[j])
c[j][i]=c[i][j]=min(c[i][j],flow);
int x=l,y=r;
for (i=l; i<=r; i++)
if (vis[a[i]]) b[x++]=a[i]; else b[y--]=a[i];
for (i=l; i<=r; i++) a[i]=b[i];
if (l<x) solve(l,x-1); if (y<r) solve(y+1,r);
}
int main(){
int cas; scanf("%d",&cas);
while (cas--){
scanf("%d%d",&n,&m); int i,x,y,z;
tot=1; memset(fst,0,sizeof(fst));
memset(c,0x3f,sizeof(c));
for (i=1; i<=m; i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z); add(y,x,z);
}
for (i=1; i<=n; i++) a[i]=i; solve(1,n);
scanf("%d",&m);
while (m--){
int ans=0,j; scanf("%d",&x);
for (i=2; i<=n; i++)
for (j=1; j<i; j++) if (c[i][j]<=x) ans++;
printf("%d\n",ans);
}
puts("");
}
return 0;
}
by lych
2016.4.15
这篇博客介绍了如何利用分治和网络流的方法解决Zjoi2011中的最小割问题。作者提到在做过ZJOI2016的题目后,这个问题变得直观。虽然无法提供证明,但提供了AC代码。

被折叠的 条评论
为什么被折叠?



