题解:分治求最小割。
【l……r】里任意找两个作为s、t(不妨把s设为l位置上的点,t设为r位置上的点)求最小割,两层for循环枚举修改map[i][j]即两点间最小割值。
然后一部分属于S集,一部分属于T集,分治【l,L】,【R,r】,每次求完最小割值都全局进行修改。
最后每次询问暴力做就好了,无需任何优化即可AC。
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200
#define M 7000
#define inf 0x3f3f3f3f
#define ci crs[i]
#define cj crs[j]
using namespace std;
struct KSD
{
int v,len,next,flow;
}e[M];
int head[N],cnt;
inline void add(int u,int v,int len)
{
cnt++;
e[cnt].v=v;
e[cnt].flow=e[cnt].len=len;
e[cnt].next=head[u];
head[u]=cnt;
}
inline void resume(){for(int i=2;i<=cnt;i++)e[i].len=e[i].flow;}
int crs[N],tcrs[N];
bool flag[N];
int map[N][N];
int d[N],s,t;
int n,m;
queue<int>q;
bool bfs()
{
int i,u,v;
memset(d,0,sizeof(d));
while(!q.empty())q.pop();
q.push(s),d[s]=1;
while(!q.empty())
{
u=q.front(),q.pop();
for(i=head[u];i;i=e[i].next)
{
v=e[i].v;
if(e[i].len&&!d[v])
{
d[v]=d[u]+1;
if(v==t)return 1;
q.push(v);
}
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x==t)return flow;
int remain=flow,i,v,k;
for(i=head[x];i&&remain;i=e[i].next)
{
if(d[v=e[i].v]==d[x]+1&&e[i].len)
{
k=dinic(v,min(remain,e[i].len));
if(!k)d[v]=0;
e[i].len-=k,e[i^1].len+=k;
remain-=k;
}
}
return flow-remain;
}
void dfs(int x)
{
int i,v;
flag[x]=1;
for(i=head[x];i;i=e[i].next)if(!flag[v=e[i].v]&&e[i].len)dfs(v);
}
void dar(int l,int r) // divide and rule 分治
{
int i,j,maxflow=0;
s=crs[l],t=crs[r];
resume();
while(bfs())maxflow+=dinic(s,inf);
memset(flag,0,sizeof(flag)),dfs(s);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)
if(flag[ci]^flag[cj])
map[ci][cj]=min(map[ci][cj],maxflow);
int L=l-1,R=r+1;
for(i=l;i<=r;i++)(flag[ci]?tcrs[++L]:tcrs[--R])=ci;
for(i=l;i<=r;i++)crs[i]=tcrs[i];
if(l<L)dar(l,L);
if(R<r)dar(R,r);
}
int main()
{
int i,j,k,g;
int a,b,c;
for(scanf("%d",&g);g--;)
{
cnt=1;
memset(head,0,sizeof(head));
memset(map,0x3f,sizeof(map));
for(scanf("%d%d",&n,&m),cnt=1;m--;)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
for(i=1;i<=n;i++)crs[i]=i;
dar(1,n);
for(scanf("%d",&m);m--;)
{
scanf("%d",&k);
int ans=0;
for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(map[i][j]<=k)ans++;
printf("%d\n",ans>>1);
}
puts("");
}
return 0;
}
:SW算法求全局最小割,然后两边任意点对间的最小割值肯定都是这个值,然后两边分治。
SW算法相关博客(点此即链接):话说我学SW就是想用来解决这个的233……
这个很有正确性的样子嘛,为什么会挂呢?
就姑且留给读者思考吧,这个是确实有问(fan)题(li)的(跟SW算法实现过程以及分治后的一些值有关系)
代码(WA):
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 160
#define inf 0x3f3f3f3f
using namespace std;
int n,m,q,crs[N],tcrs[N],cnt;
struct ANS
{
int x,sum;
ANS(int _x=0,int _sum=0):x(_x),sum(_sum){}
bool operator < (const ANS &a)const{return x<a.x;}
}ans[N];
int map[N][N];
int dis[N],f[N];
bool vis[N],flag[N];
int next[N],final[N];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void Stoer_Wagner(int l,int r)
{
int ret=inf;
int i,j,_n,rnd;
int u,v,temp,lastu=0;
for(i=l;i<=r;i++)f[i]=next[i]=final[i]=i,flag[i]=0;
for(_n=r-l+1;_n>1;_n--)
{
for(i=l;i<=r;i++)dis[i]=vis[i]=0;
for(rnd=1;rnd<=_n;rnd++)
{
lastu=u;
temp=-1;
for(i=l;i<=r;i++)if(f[i]==i&&!vis[i])
{
if(temp<dis[i])temp=dis[i],u=i;
}
vis[u]=1;
bool flag=0;
for(j=u;;j=next[j])
{
if(j==final[u])flag=1;
for(i=l;i<=r;i++)if(map[crs[u]][crs[i]])
{
v=find(i);
if(v!=u)dis[v]+=map[crs[u]][crs[i]];
}
if(flag)break;
}
}
if(ret>=dis[u])
{
for(i=l;i<=r;i++)flag[i]=(f[i]!=i||i==u);
ret=dis[u];
}
f[u]=lastu;
next[final[lastu]]=u;
final[lastu]=final[u];
}
int L=l-1,R=r+1;
for(i=l;i<=r;i++)
{
if(flag[i])tcrs[++L]=crs[i];
else tcrs[--R]=crs[i];
}
for(i=l;i<=r;i++)crs[i]=tcrs[i];
ans[++cnt]=ANS(ret,(R-l)*(r-L));
if(R-l>1)Stoer_Wagner(l,L);
if(r-L>1)Stoer_Wagner(R,r);
return ;
}
int sum[N];
int main()
{
// freopen("test.in","r",stdin);
int i,k,g;
int a,b,c;
for(scanf("%d",&g);g--;)
{
memset(map,0,sizeof(map));
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)crs[i]=i;
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(a!=b)map[a][b]+=c,map[b][a]+=c;
}
Stoer_Wagner(1,n);
sort(ans+1,ans+cnt+1);
for(i=1;i<=cnt;i++)sum[i]=sum[i-1]+ans[i].sum;
scanf("%d",&q);
while(q--)
{
scanf("%d",&k);
int l=1,r=cnt,mid,ret;
while(l<r)
{
if(r-l<=3)
{
ret=0;
for(i=l;i<=r;i++)if(ans[i].x<=k)ret=i;
break;
}
mid=l+r>>1;
if(ans[i].x<=k)l=mid;
else r=mid-1;
}
printf("%d\n",sum[ret]);
}
}
return 0;
}
【BZOJ2229】【ZJOI2011】最小割 {没有错,这道题的算法跟题帽是一样的!!!}
原文:http://blog.csdn.net/vmurder/article/details/42525043