列表

详情


NC20495. [ZJOI2011]最小割

描述

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

输入描述

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

输出描述

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

示例1

输入:

1
5 0
1
0

输出:

10

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(g++ 7.5.0) 解法, 执行用时: 129ms, 内存消耗: 1104K, 提交时间: 2022-12-31 11:00:54

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N = 210, M = 3010 * 4, INF = 0x3f3f3f3f;
// 边数要开四倍 
int h[N], e[M], ne[M], f[M], idx, d[N], n, m, S, T, cur[M];
void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], f[idx] = c, h[a] = idx ++;
    e[idx] = a, ne[idx] = h[b], f[idx] = 0, h[b] = idx ++; 
}
bool bfs()
{
    queue<int>q;
    memset(d, -1, sizeof d);
    q.push(S);
    d[S] = 0;
    cur[S] = h[S];
    while(!q.empty())
    {
        auto t = q.front();
        q.pop();
        
        for(int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if((d[j] == -1) && f[i])
            {
                d[j] = d[t] + 1;
                cur[j] = h[j];
                if(j == T) return true;
                q.push(j);
            }
        }
    }
    return false;
}

int find(int u, int limit)
{
    if(u == T) return limit;
    int flow = 0;
    for(int i = cur[u]; ~i && flow < limit; i = ne[i])
    {
        int j = e[i];
        cur[u] = i;
        if((d[j] == d[u] + 1) && f[i])
        {
            int t = find(j, min(f[i], limit - flow));
            if(!t) d[j] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

int dinic()
{
	for(int i = 0; i < idx; i += 2) f[i] += f[i ^ 1], f[i ^ 1] = 0; // 退流
    int r = 0, flow;
    while(bfs()) while(flow = find(S, 0x3f3f3f3f)) r += flow;
    return r;
}

int id[N], ans[N][N], tmp1[N], tmp2[N];

void work(int l, int r)
{
	if(l == r) return;
	S = id[l], T = id[l + 1];
	int t = dinic(), s = id[l], tt = id[l + 1];
	ans[S][T] = ans[T][S] = t;

	int cnt1 = 0, cnt2 = 0;

	for(int i = l; i <= r; i ++ )
		if(d[id[i]] != -1) tmp1[++ cnt1] = id[i];
		else tmp2[++ cnt2] = id[i];
	for(int i = 1; i <= cnt1; i ++ ) id[i + l - 1] = tmp1[i];
	for(int i = 1; i <= cnt2; i ++ ) id[cnt1 + l + i - 1] = tmp2[i];
	work(l, l + cnt1 - 1);
	work(l + cnt1, r);

	for(int i = 1; i <= cnt1; i ++)
		for(int j = 1; j <= cnt2; j ++ )
		{
			int a = id[i + l - 1], b = id[j + cnt1 + l - 1];
			ans[b][a] = ans[a][b] = min({ans[a][s], ans[s][tt], ans[tt][b]});
		}
}
void solve()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);
	idx = 0;
	memset(ans, 0x3f, sizeof ans);
	for(int i = 1; i <= m; i ++ )
	{
		int u, v, w;
		cin >> u >> v >> w;
		add(u, v, w);
		add(v, u, w);
	}
	for(int i = 1; i <= n; i ++ ) id[i] = i;

	work(1, n);
	int q;
	cin >> q;

	while(q -- )
	{
		int x, res = 0;
		cin >> x;
		for(int i = 1; i <= n; i ++ )
			for(int j = i + 1; j <= n; j ++ ) if(ans[i][j] <= x) res ++;
		cout << res << '\n';
	}
	cout << '\n';
}
signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T = 1;
	cin >> T;
	while(T -- ) solve();
}

C++ 解法, 执行用时: 90ms, 内存消耗: 596K, 提交时间: 2022-07-04 22:16:46

#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
#define maxn 155
#define maxm 6005
using namespace std;
inline void read(int &a){
	char c;while(!isdigit(c=getchar()));
	for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
const int inf = 0x3f3f3f3f;
int n,m,S,T;
int fir[maxn],cur[maxn],dis[maxn],nxt[maxm],to[maxm],tot=1,cap[maxm],sum;
inline void line(int x,int y,int z,int rz=0){
	nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,cap[tot]=z;
	nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,cap[tot]=rz;
}
queue<int>q;
bool bfs()
{
	memset(dis,0,(n+1)<<2);
	dis[T]=1,q.push(T);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=fir[u];i;i=nxt[i]) if(cap[i^1]&&!dis[to[i]]){
			dis[to[i]]=dis[u]+1;
			q.push(to[i]);
		}
	}
	return dis[S];
}
int dfs(int u,int lim)
{
	if(u==T) return lim;
	int need=lim,delta;
	for(int &i=cur[u];i;i=nxt[i]) if(cap[i]&&dis[u]==dis[to[i]]+1){
		delta=dfs(to[i],min(cap[i],need));
		cap[i]-=delta,cap[i^1]+=delta;
		if(!(need-=delta)) break;
	}
	return lim-need;
}
int Dinic(){
	int flow=0;
	while(bfs()) memcpy(cur,fir,(n+1)<<2),flow+=dfs(S,inf);
	return flow;
}
int Test,Q,x,y,z,s,ans[maxn][maxn],id[maxn],tmp[maxn];
void solve(int l,int r){
	if(l>=r) return;
	for(int i=2;i<=tot;i+=2) cap[i]=cap[i+1]=(cap[i]+cap[i+1])>>1;
	S=id[l],T=id[r];
	int ret=Dinic();
	for(int i=1;i<=n;i++) if(dis[i])
		for(int j=1;j<=n;j++) if(!dis[j])
			ans[i][j]=ans[j][i]=min(ans[i][j],ret);
	int h=l-1,t=r;
	for(int i=l;i<=r;i++) if(dis[id[i]]) tmp[++h]=id[i]; else tmp[t--]=id[i];
	for(int i=l;i<=r;i++) id[i]=tmp[i];
	solve(l,h),solve(h+1,r);
}

signed main()
{
	read(Test);
	while(Test--)
    {
		memset(fir,0,sizeof fir),tot=1;
		memset(ans,0x3f,sizeof ans);
		read(n),read(m);
		for(int i=1;i<=m;i++) read(x),read(y),read(z),line(x,y,z,z);
		for(int i=1;i<=n;i++) id[i]=i;
		solve(1,n);
		read(Q);
		while(Q--){
			read(x),s=0;
			for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(ans[i][j]<=x) s++;
			printf("%d\n",s);
		}
		puts("");
	}
}

上一题