• [ZJOI2011] 最小割


    一、前言

    最小割树练习板题,浓度更纯的板题就不写题解了吧,虽然都挺板的。

    注意本博客只讲方法不讲证明,原因下面有。

    二、题目

    洛谷

    三、讲解

    (一)、坑点

    首先注意到这件事:

    在两组测试数据之间需要输出一行空行。

    然后我们开始讲。

    (二)、科技:最小割树

    这小标题有种3A大作的感觉。

    首先我们要定义无向图中两个点的最小割:使得它们不连通要割的边的最小权值和。

    存在这么一棵带边权的树:点与点之间路径上权值的最小值为这两个点在原图中的最小割,我们称这棵树为最小割树。

    构造方法是每次随便挑两个点作为源汇点跑最小割,然后连树边,权值就是这个最小割,把源汇点两边的点分别递归下去做,可以发现要跑 \(O(n)\) 次网络流,一般来说时间的瓶颈就在这里。

    至于为啥存在这样一棵树,以及为啥这样构造,这样构造为啥是对的?一律不解释,问就是懒。

    想了解可以看2016年的集训队论文《浅谈无向图最小割问题的一些算法和应用》,应该比我讲得更权威更清楚。没想到吧,这就是我偷懒的借口!

    既然建树的复杂度都这么高了,往往询问都可以直接在这棵树上暴力做,因此题目都比较板。

    (三)、正题

    建出最小割树之后,求出两两之间最小割,然后每次询问 \(O(n^2)\) 回答即可。

    时间复杂度 \(O(n^3m+qn^2)\)

    四、代码

    //12252024832524
    #include <bits/stdc++.h>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 155;
    const int MAXM = 3005;
    const LL INF = 1ll << 60;
    int n,m;
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    int head[MAXN],tot = 1;
    struct edge
    {
    	int v,w,nxt;
    }e[MAXM<<1];
    void Add_Edge(int u,int v,int w)
    {
    	e[++tot] = edge{v,w,head[u]};
    	head[u] = tot;
    }
    void Add_Double_Edge(int u,int v,int w) 
    {
    	Add_Edge(u,v,w);
    	Add_Edge(v,u,w);
    }
    
    int dis[MAXN],cur[MAXN];
    bool bfs(int S,int T)
    {
    	for(int i = 1;i <= n;++ i) dis[i] = n+1;
    	queue<int> q; q.push(S); dis[S] = 0;
    	while(!q.empty())
    	{
    		int x = q.front(); q.pop();
    //		printf("bfs %d %d %d\n",x,S,T);
    		for(int i = head[x],v; i ;i = e[i].nxt)
    			if(e[i].w && dis[x]+1 < dis[v = e[i].v]) dis[v] = dis[x]+1,q.push(v);
    	}
    	return dis[T] <= n;
    }
    LL dfs(int x,LL flow,int T)
    {
    	if(x == T) return flow;
    	LL ret = 0;
    	for(int &i = cur[x],v; i ;i = e[i].nxt)
    		if(dis[x]+1 == dis[v = e[i].v] && e[i].w)
    		{
    			LL dz = dfs(v,Min(0ll+e[i].w,flow-ret),T);
    			e[i].w -= dz; e[i^1].w += dz;
    			if((ret += dz) == flow) break;
    		}
    	return ret;
    }
    LL dinic(int S,int T)
    {
    	for(int i = 2;i <= tot;i += 2) e[i^1].w = e[i].w = (0ll + e[i].w + e[i^1].w) >> 1;
    	LL ret = 0;
    	while(bfs(S,T)) 
    	{
    		for(int i = 1;i <= n;++ i) cur[i] = head[i]; 
    		ret += dfs(S,INF,T);
    	} 
    	return ret;
    }
    
    vector<pair<int,LL> > G[MAXN];
    int ID[MAXN];
    bool vis[MAXN];
    void findp(int x)
    {
    	vis[x] = 1;
    	for(int i = head[x],v; i ;i = e[i].nxt)
    		if(e[i].w && !vis[v = e[i].v]) 
    			findp(v);
    }
    void solve(int l,int r)
    {
    	if(l >= r) return;
    	LL mf = dinic(ID[l],ID[l+1]);
    	G[ID[l]].emplace_back(make_pair(ID[l+1],mf));
    	G[ID[l+1]].emplace_back(make_pair(ID[l],mf));
    	for(int i = 1;i <= n;++ i) vis[i] = 0;
    	findp(ID[l]);
    	sort(ID+l,ID+r+1,[](int x,int y){return vis[x] < vis[y];});
    	for(int i = l;i <= r;++ i)
    		if(vis[ID[i]]) {solve(l,i-1);solve(i,r);break;}
    }
    int rt;
    LL cut[MAXN][MAXN];
    void dfs2(int x,int fa,LL d)
    {
    	cut[rt][x] = d;
    	for(auto &A : G[x])
    		if(A.first != fa)
    			dfs2(A.first,x,Min(d,A.second));
    }
    
    int main()
    {
    //	freopen("10.in","r",stdin);
    //	freopen("mine.out","w",stdout);
    	for(int T = Read(); T ;-- T)
    	{
    		n = Read(); m = Read(); tot = 1;
    		for(int i = 1;i <= n;++ i) head[i] = 0,G[i].clear(),ID[i] = i;
    		for(int i = 1,u,v;i <= m;++ i) u = Read(),v = Read(),Add_Double_Edge(u,v,Read());
    		solve(1,n);
    		for(int i = 1;i <= n;++ i) rt = i,dfs2(i,0,INF);
    		for(int Q = Read(); Q ;-- Q)
    		{
    			LL val = Read(),ans = 0;
    			for(int i = 1;i <= n;++ i)
    				for(int j = i+1;j <= n;++ j)
    					if(cut[i][j] <= val) ++ans;
    			Put(ans,'\n');
    		}
    		putchar('\n');
    	}
    	return 0;
    }
    
  • 相关阅读:
    遇见SQL(2)
    遇见SQL(1)
    JAVA自学笔记(10)—Stream流、方法引用
    JAVA自学笔记(9)——网络通信、函数式接口
    JAVA自学笔记(7)—文件
    JAVA自学笔记(6)—异常、线程、函数式编程
    Python--模块Module
    Python--软件目录结构
    Python--迭代器
    Python--生成器
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15921111.html
Copyright © 2020-2023  润新知