【BZOJ2229】[Zjoi2011]最小割 最小割树

时间:2023-03-09 18:29:03
【BZOJ2229】[Zjoi2011]最小割 最小割树

【BZOJ2229】[Zjoi2011]最小割

Description

小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。 对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割” 现给定一张无向图,小白有若干个形如“图中有多少对点它们的最小割的容量不超过x呢”的疑问,小蓝虽然很想回答这些问题,但小蓝最近忙着挖木块,于是作为仍然是小蓝的好友,你又有任务了。

Input

输入文件第一行有且只有一个正整数T,表示测试数据的组数。 对于每组测试数据, 第一行包含两个整数n,m,表示图的点数和边数。 下面m行,每行3个正整数u,v,c(1<=u,v<=n,0<=c<=106),表示有一条权为c的无向边(u,v) 接下来一行,包含一个整数q,表示询问的个数 下面q行,每行一个整数x,其含义同题目描述。

Output

对于每组测试数据,输出应包括q行,第i行表示第i个问题的答案。对于点对(p,q)和(q,p),只统计一次(见样例)。

两组测试数据之间用空行隔开。

Sample Input

1
5 0
1
0

Sample Output

10
【数据范围】
对于100%的数据 T<=10,n<=150,m<=3000,q<=30,x在32位有符号整数类型范围内。
图中两个点之间可能有多条边

题解:最小割树,就是利用分治,将求n^2个最小割变成求n次最小割。

我们在区间l,r中随便找两个点,求出最小割,将与S一个集合的放到左边,与T一个集合的放到右面,然后更新所有点对之间的最小割,在继续分治处理左右两边。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
int n,m,Q,S,T,cnt;
int to[10010],next[10010],val[10010],head[1000],d[300],p[300],pp[300],vis[300],map[300][300];
int s[100100];
queue<int> q;
int dfs(int x,int mf)
{
if(x==T) return mf;
int i,k,temp=mf;
for(i=head[x];i!=-1;i=next[i])
{
if(d[to[i]]==d[x]+1&&val[i])
{
k=dfs(to[i],min(temp,val[i]));
if(!k) d[to[i]]=0;
val[i]-=k,val[i^1]+=k,temp-=k;
if(!temp) break;
}
}
return mf-temp;
}
int bfs()
{
while(!q.empty()) q.pop();
memset(d,0,sizeof(d));
d[S]=1,q.push(S);
int i,u;
while(!q.empty())
{
u=q.front(),q.pop();
for(i=head[u];i!=-1;i=next[i])
{
if(!d[to[i]]&&val[i])
{
d[to[i]]=d[u]+1;
if(to[i]==T) return 1;
q.push(to[i]);
}
}
}
return 0;
}
int dinic()
{
int ret=0;
while(bfs()) ret+=dfs(S,1<<30);
return ret;
}
void DFS(int x)
{
vis[x]=1;
for(int i=head[x];i!=-1;i=next[i]) if(!vis[to[i]]&&val[i]) DFS(to[i]);
}
void solve(int l,int r)
{
if(l==r) return ;
S=p[l],T=p[r];
int i,j,h1=l,h2=r,mf;
for(i=0;i<cnt;i+=2) val[i]=val[i^1]=val[i]+val[i^1]>>1;
mf=dinic();
memset(vis,0,sizeof(vis));
DFS(S);
for(i=1;i<=n;i++) if(vis[i])
for(j=1;j<=n;j++) if(!vis[j])
map[i][j]=map[j][i]=min(map[i][j],mf);
for(i=l;i<=r;i++)
{
if(vis[p[i]]) pp[h1++]=p[i];
else pp[h2--]=p[i];
}
for(i=l;i<=r;i++) p[i]=pp[i];
solve(l,h2),solve(h1,r);
}
int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
void add(int a,int b,int c)
{
to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
to[cnt]=a,val[cnt]=c,next[cnt]=head[b],head[b]=cnt++;
}
void work()
{
memset(head,-1,sizeof(head)),cnt=0;
memset(map,0x3f,sizeof(map));
n=rd(),m=rd();
int i,j,a,b,c,l,r,mid;
for(i=1;i<=n;i++) p[i]=i;
for(i=1;i<=m;i++) a=rd(),b=rd(),c=rd(),add(a,b,c);
solve(1,n);
for(s[0]=0,i=1;i<=n;i++) for(j=i+1;j<=n;j++) s[++s[0]]=map[i][j];
sort(s+1,s+s[0]+1);
Q=rd();
for(i=1;i<=Q;i++)
{
a=rd(),l=1,r=s[0]+1;
while(l<r)
{
mid=l+r>>1;
if(s[mid]<=a) l=mid+1;
else r=mid;
}
printf("%d\n",l-1);
}
}
int main()
{
int T=rd();
while(T--)
{
work();
if(T) printf("\n");
}
}