首页 > 其他分享 >CF343E Pumping Stations 题解

CF343E Pumping Stations 题解

时间:2024-03-12 22:34:29浏览次数:24  
标签:return val vis CF343E 题解 Pumping int ans include

题意:

给定一张无向带权图,求一个排列 \(p\) 使得 \(\sum_{i=2}^n \operatorname{mincut}(p_{i - 1}, p_i)\) 最大。输出一种方案。

\(n \le 200, m \le 1000\)。

思路:

首先这种最小割相关的肯定是最小割树,建树需要 \(O(n^3 m)\),由于 \(Dinic\) 实际上跑不满,所以时间完全够。

然后考虑检出最小割树,转化为对于一棵树,求一个排列,\(val(i,j)\) 定义为 \(i\) 到 \(j\) 路径上最小边权,使 \(\sum_{i=2}^nval(p_i, p_{i - 1})\) 最大。

对于这种问题,我们考虑构造一个上界,不难发现,最大边权最多贡献一次,沿着这个思路,我们猜测答案是恰好每条边贡献一次。

我们先构造一个方案,找到最小边,然后递归两边处理,保证只有一次经过了这条最小边即可。

考虑如何证明这是最优的。

对树的节点数采用数学归纳法,我们找到最小的边 \((u,v)\),显然,经过 \((u,v)\) 一次以上不如只经过 \((u,v)\) 一次。所以我们可以发现最右情况 \((u,v)\) 只会被经过 \(1\) 次,而根据归纳法,被分开的两个部分也是边权值和为答案,所以我们就可以证出来这个结论。

然后就是最小割树剪出来加上一个简单的分治即可。

点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 205;
const int inf = 0x3f3f3f3f;

int n, m;
struct Edge {
	int to, val, rev;
	Edge (int _to = 0, int _val = 0, int _rev = 0) :
		to(_to), val(_val), rev(_rev) {}
};
vector<Edge> e[N], eG[N];
void addEdge(int u, int v, int w) {
	eG[u].push_back(Edge(v, w, (int)eG[v].size()));
	eG[v].push_back(Edge(u, w, (int)eG[u].size() - 1));
}



int d[N] = {0};
bool bfs(int s, int t) {
	memset(d, inf, sizeof d);
	queue<int> q;
	q.push(s), d[s] = 0;
	while (!q.empty()) {
		int h = q.front();
		q.pop();
		for (auto i: e[h])
			if (i.val > 0 && d[i.to] > d[h] + 1) 
				d[i.to] = d[h] + 1, q.push(i.to);
	}
	return d[t] != inf;
}

int cur[N] = {0};
int dfs(int x, int t, int f) {
	if (x == t)
		return f;
	for (int i = cur[x]; i < (int)e[x].size(); i = ++cur[x])
		if (e[x][i].val > 0 && d[e[x][i].to] == d[x] + 1) {
			int fl = dfs(e[x][i].to, t, min(e[x][i].val, f));
			if (fl > 0) {
				e[x][i].val -= fl;
				e[e[x][i].to][e[x][i].rev].val += fl;
				return fl;
			}
		}
	d[x] = -1;
	return 0; 
}

int Dinic(int s, int t) {
	//init();
	int ans = 0;
	while (bfs(s, t)) {
		memset(cur, 0, sizeof cur);
		int ad = 0;
		while ((ad = dfs(s, t, inf)) != 0)
			ans += ad;
	}
	return ans;
}

void init() {
	for (int i = 1; i <= n; i++)
		e[i] = eG[i];
}

int a[N] = {0}, b[N] = {0};

struct Edge_Tree {
	int to, val;
	Edge_Tree (int _to = 0, int _val = 0) :
		to(_to), val(_val) {} 
};
vector<Edge_Tree> t[N];

void add(int u, int v, int w) {
	t[u].push_back(Edge_Tree(v, w));
	t[v].push_back(Edge_Tree(u, w));
}

int slv(int l, int r) {
	if (l + 1 >= r)
		return 0;
	int S = a[l], T = a[l + 1];
	init();
	int ans = Dinic(S, T);
	add(S, T, ans);
	int ll = l, rr = r;
	for (int i = l; i < r; i++)
		if (d[a[i]] != inf)
			b[ll++] = a[i];
		else
			b[--rr] = a[i];
	for (int i = l; i < r; i++)
		a[i] = b[i];
	ans += slv(l, ll) + slv(rr, r);
	return ans;
}

bool vis[N] = {false};

int ans[N] = {0}, len = 0;

void srh(int x, int pr, int &A, int &B, int &mn) {
	for (auto i: t[x])
		if (i.to != pr && !vis[i.to]) {
			if (mn > i.val) 
				mn = i.val, A = x, B = i.to;
			srh(i.to, x, A, B, mn);
		}
}

void mrk(int x) {
	vis[x] = true;
	for (auto i: t[x])
		if (!vis[i.to])
			mrk(i.to);
}

void getans(int x) {//在当前还没有 vis 的 x 所在联通块找到答案 
	int A = 0, B = 0, mn = 2e9;
	srh(x, 0, A, B, mn);
	if (A + B == 0) 
		ans[++len] = x;
	else {
		vis[B] = true;
		getans(A);
		vis[B] = false;
		getans(B);
	}
	mrk(x);
} 

int main() {
	cin >> n >> m;
	for (int i = 1, u, v, w; i <= m; i++) {
		cin >> u >> v >> w;
		addEdge(u, v, w);
	}
	for (int i = 1; i <= n; i++)
		a[i] = i;
	cout << slv(1, n + 1) << endl;
	getans(1);
	for (int i = 1; i <= n; i++)
		cout << ans[i] << " ";
	cout << endl;
	return 0;
} 

标签:return,val,vis,CF343E,题解,Pumping,int,ans,include
From: https://www.cnblogs.com/rlc202204/p/18069508

相关文章

  • 联合省选 2024 题解
    D1T1-P10217[省选联考2024]季风约定:令\(a_i,b_i\)代替原来的\(x_i,y_i\),避免变量重名。显然地,考虑按\(m\bmodn\)的值分类,那么每一类都相当于若干个整段\(+\)一段前缀。假设加上的是\([1,i]\)前缀,选了\(m'\)个整段,那么\(a\)的和可以表示为\(m'\timessuma_n+......
  • LeetCode[题解] 1261. 在受污染的二叉树中查找元素
    首先我们看原题给出一个满足下述规则的二叉树:root.val==0如果 treeNode.val==x 且 treeNode.left!=null,那么 treeNode.left.val==2*x+1如果 treeNode.val==x 且 treeNode.right!=null,那么 treeNode.right.val==2*x+2现在这个二叉树受到「污......
  • 题解:AT_abc194_d [ABC194D] Journey
    题意简述有一张\(n\)个节点的无边图,图在连通之前每次随机抽取一个点然后建边,求期望操作次数(即最大多少次)。思路这道题似乎和图论没什么关系,首先我们看\(n\)个球抽出一个的概率是\(\frac{1}{n}\),那么抽第二个时概率是\(\frac{1}{n-1}\)以此类推。所以最坏情况下就是抽第......
  • 力扣hot100题解(python版69-73题)
    69、有效的括号给定一个只包括'(',')','{','}','[',']'的字符串s,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。示例1:输入:s="()"输出:true示例2:输入:s="()[......
  • 【解题报告】THOI2023核心素养一题解
    THOI核心素养一题解展开目录目录THOI核心素养一题解比赛结果:A沙粒的记忆B远星的守望C国王的诞生E坏齿的舞曲F山麓的回音EXTRA群星的闪耀比赛页面(题目已公开,邀请码:yspm)赛时公告板看得出来出了相当多锅(由于D出锅严重,现已撤下,比赛延长10min.还请各位海涵(为什么延长......
  • 【计算机网络01】网卡——链接5G热点网速较慢问题解决方法。
    计算机网络:网卡一、网卡带宽查询二、高速带宽设置一.在电脑连接手机热点的时候,查询网络属性,找到网络频带是2.4GHz,带宽是72(Mbps):查找原因,发现是手机热点页面中AP频带设置的原因,AP频带设置成了2.4GHz:二.更改手机热点页面中AP频带,将AP频带设置成了5GHz频段:再在电......
  • Dwango Programming Contest 6th D 题解
    正好测试一下专栏的题解系统。我省选寄了都怪洛谷/fn/fn/fn/fn/fn/fn/fn题解显然可以对于所有关系建有向边,显然是基环外向树森林。由于是字典序最小,因此找到最小的上一个点没有直接连向边的点一定最优。但是有时取最优会导致最后无法选完,我们考虑无法选完的情况。第一种是......
  • AtCoder Beginner Contest 344 A-G 题解
    AtCoderBeginnerContest344A-SpoilerQuestion删除两个|之间的字符Solution按照题意模拟即可Code#include<bits/stdc++.h>usingnamespacestd;intmain(){strings;cin>>s;stringp1,p2;for(inti=0;i<s.size();i++){......
  • LeetCode - 高频SQL50题(基础版)部分题解(上)
    1581.进店却未进行过交易的顾客原题:https://leetcode.cn/problems/customer-who-visited-but-did-not-make-any-transactions/题意:有一些顾客可能光顾了购物中心但没有进行交易。请你编写一个解决方案,来查找这些顾客的ID(customer_id),以及他们只光顾不交易的次数(count_no_trans......
  • node: /lib64/libm.so.6: version `GLIBC_2.27‘ not found问题解决方案
    场景centos7服务器使用nvm安装的node之后,只要使用npm或者node,均会出现以下问题。npm-vnode:/lib64/libm.so.6:version`GLIBC_2.27'notfound(requiredbynode)node:/lib64/libc.so.6:version`GLIBC_2.25'notfound(requiredbynode)node:/lib64/libc.so.6:ver......