The 2019 ICPC Asia Shanghai Regional Contest(重现赛) M-Blood Pressure Game(费用流&算贡献)

链接:https://ac.nowcoder.com/acm/contest/4370/M
来源:牛客网
 

题目描述

1Gugulu, a JBer, who was an ACMer one year ago, comes to Shanghai University taking part in the International Collegiate Programming Contest again. However, every time when Gugulu came to Shanghai University, he always got an Iron Medal and would consider this competition JB-like. To release his pain, Gugulu would go to the Shanghai Disneyland Park to have fun in taking the Roller Coaster. Gugulu loves the feeling with high-level blood pressure so much which makes him feel as a happy flappy bird who forgets all the Wrong Answers and Time Limit Exceededs etc.

The 2019 ICPC Asia Shanghai Regional Contest(重现赛) M-Blood Pressure Game(费用流&算贡献)_第1张图片

We can regard the path of the Roller Coaster as a list of turning points with different heights which can be represented as an array {a1,a2,a3,…,an}\{a_1, a_2, a_3, …, a_n\}{a1​,a2​,a3​,…,an​} of size n{n}n, and Gugulu's final blood pressure after the game of the Roller Coaster is counted as the sum of all absolute values of the differences between the n−1{n - 1}n−1 pairs of adjacent array numbers, i.e. ∑i=1n−1∣ai−ai+1∣\displaystyle \sum_{i=1}^{n-1} \left| a_i - a_{i+1} \right|i=1∑n−1​∣ai​−ai+1​∣.

Gugulu always got Iron Medals and is always getting Iron Medals, which makes him keep taking the Roller Coaster over and over again. However, as playing more games, his threshold on the value of blood pressure which can make himself happy is keeping increasing. As a result, the Roller Coaster of Shanghai Disneyland Park can hardly meet Gugulu's need anymore.

Therefore, Gugulu decides to add a set of m extra turning points in any order into this path, however, to consider about the reality on the distance of the original path, he can add at most one turning point into any original position between any two original elements in the array (and at most one in the head, at most one in the tail). Gugulu wants to make his blood pressure as high as possible, and he wants to know how much his blood can reach at most when he has added {1,2,3,…,m}{\{1, 2, 3, …, m\}}{1,2,3,…,m} nextra Roller Coaster turning points into the path.

You, another JBer, are sure that Gugulu is clever enough to get the highest blood pressure as he can. It is very important for you to calculate the exact numbers to make an appointment with a proper cardiologist in advance to save Gugulu's life. You must solve this problem! Gugulu's blood pressure is becoming out of the control!

输入描述:

The first line of the input gives the number of test cases, T\mathbf{T}T (1≤T≤10001 \leq \mathbf{T} \leq 10001≤T≤1000). T\mathbf{T}T test cases follow.
For each test case, the first line contains two integers, n{n}n (1≤n≤6001 \leq n \leq 6001≤n≤600) and m{m}m (1≤m≤n+11 \leq m \leq n+11≤m≤n+1) 
Then, in second line, there are n{n}n integers {a1,a2,a3,…,an}\{a_1, a_2, a_3, …, a_n\}{a1​,a2​,a3​,…,an​} (1≤ai≤109)(1 \leq a_i \leq 10^9)(1≤ai​≤109), denoting the n{n}n original Roller Coaster turning points. Then, in the third line, there are m{m}m integers {b1,b2,b3,…,bm}\{b_1, b_2, b_3, …, b_m\}{b1​,b2​,b3​,…,bm​} (1≤bi≤109)(1 \leq b_i \leq 10^9)(1≤bi​≤109), denoting the m{m}m addition Roller Coaster turning points to be added. 
As it is an exciting Roller Coaster, it is guaranteed that all n+m{n + m}n+m integers in the two arrays are pairwise different.
There are at most 10{10}10 test cases whose n{n}n is more than 100{100}100.

输出描述:

For each test case, output three lines. The first line contains "Case #x:", where x{x}x is the test case number (starting from 1{1}1). In the second line, there should be m{m}m integers which represent how much Gugulu's blood pressure could reach if he has added {1, 2, 3, …, m} extra Roller Coaster turning points into the path. In the third line, there should be n+m{n + m}n+m integers which represent the heights of the final Roller Coaster path if he has added all m{m}m extra  turning points. If there are several solutions, output any of them.

示例1

输入

复制6 2 3 5 11 10 3 1 4 1 1 2 3 4 5 4 2 1 2 3 4 5 6 4 5 1 2 3 4 5 6 7 8 9 4 4 10 50 3 6 1 9 23 5 4 2 10 50 3 6 9 23

6
2 3
5 11
10 3 1
4 1
1 2 3 4
5
4 2
1 2 3 4
5 6
4 5
1 2 3 4
5 6 7 8 9
4 4
10 50 3 6
1 9 23 5
4 2
10 50 3 6
9 23

输出

复制Case #1: 16 22 27 10 5 1 11 3 Case #2: 9 1 5 2 3 4 Case #3: 11 15 1 6 2 5 3 4 Case #4: 17 27 33 38 39 5 1 9 2 8 3 7 4 6 Case #5: 124 142 147 150 5 10 1 50 3 23 6 9 Case #6: 124 127 10 50 3 23 6 9

Case #1:
16 22 27 
10 5 1 11 3 
Case #2:
9 
1 5 2 3 4 
Case #3:
11 15 
1 6 2 5 3 4 
Case #4:
17 27 33 38 39 
5 1 9 2 8 3 7 4 6 
Case #5:
124 142 147 150 
5 10 1 50 3 23 6 9 
Case #6:
124 127 
10 50 3 23 6 9

题目大意:

给你一个长度为N的序列和一个长度为M的序列,你要将这M个数插入第一个序列中,两个数之间只能插一个数(可以放在最左或最右),插入的顺序随意,序列的权值为相邻数差的绝对值之和。输出第一行M个数,第i个数表示插入i个数后的最大权值,第二行输出插入M个数后最大权值的序列。

解法:

M个数有N+1个点可以连,分别对应了可以插入的N+1个位置,先算出初始序列的权值,当插入一个点之后,他的贡献就是插入后的权值-原来的权值,所以建图方法为:源点连M个点,流量为1,费用为0,M个点分别连接N+1个点,流量为1,费用为贡献,N+1个点连汇点,流量为1,费用为0,跑M次最大费用流即可,最后在残余网络中输出序列。

这道题被我们学校一队大佬一发秒了(在知道是网络流的前提下),思路都是跟他学的,我还是太菜了啊QAQ

 Accetped code

#pragma GCC optimize(3)
#include
#include
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair 
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 1210;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % Mod; b >>= 1; t = (t*t) % Mod; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }

struct Edge
{
	ll f, w;
	int r, v;
};
vector  G[N];
ll dis[N], flow[N];
ll mxflow, mxcost;
int n, m, sp, tp;
int pre[N], lst[N];
bool vis[N];
int a[N], b[N];

void Init() {
	sp = 0, tp = n + m + 2;
	for (int i = sp; i <= tp; i++)
		G[i].clear();
	mxflow = mxcost = 0;
}
void Add(int u, int v, ll f, ll w) {
	G[u].push_back({ f, w, SZ(G[v]), v });
	G[v].push_back({ 0, -w, SZ(G[u]) - 1, u });
}
bool Spfa() {   // 找到一条长短路的增广
	queue  q;
	for (int i = sp; i <= tp; i++) {
		dis[i] = -LINF;
		flow[i] = LINF;
		vis[i] = false;
	}
	q.push(sp);
	vis[sp] = true;
	dis[sp] = 0, pre[tp] = -1;

	while (!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = false;

		for (int i = 0; i < SZ(G[u]); i++) {
			ll f = G[u][i].f, w = G[u][i].w;
			int v = G[u][i].v;
			if (f && dis[v] < dis[u] + w) {
				dis[v] = dis[u] + w;
				pre[v] = u;       // 前驱点
				lst[v] = i;       // 当前边的vector下标
				flow[v] = min(flow[u], f);
				if (!vis[v]) {
					vis[v] = true;
					q.push(v);
				}
			}
		}
	}
	return pre[tp] != -1;
}

int main()
{
	int T;
	cin >> T;
	for (int Case = 1; Case <= T; Case++) {
		sc("%d %d", &n, &m);
		Init();

		ll ans = 0;
		for (int i = 1; i <= n; i++) {
			sc("%d", &a[i]);
			if (i > 1)
				ans += abs(a[i] - a[i - 1]);
		}

		// 源点->待插入点
		for (int i = 1; i <= m; i++) {
			sc("%d", &b[i]);
			Add(sp, i, 1, 0);
		}

		// 待插入点对原序列的贡献
		for (int i = 1; i <= m; i++) {
			for (int j = 1; j <= n + 1; j++) {
				ll w;
				if (j == 1)
					w = abs(b[i] - a[1]);
				else if (j == n + 1)
					w = abs(b[i] - a[n]);
				else
					w = abs(b[i] - a[j - 1]) + abs(b[i] - a[j]) - abs(a[j] - a[j - 1]);
				// 待插入->原序列
				Add(i, m + j, 1, w);
			}
		}

		// 原序列->汇点
		for (int i = 1; i <= n + 1; i++)
			Add(m + i, tp, 1, 0);

		printf("Case #%d:\n", Case);
		for (int i = 1; i <= m; i++) {
			Spfa();
			int now = tp;
			mxflow = flow[tp];
			mxcost = flow[tp] * dis[tp];
			while (now != sp) {
				G[pre[now]][lst[now]].f -= mxflow;
				G[now][G[pre[now]][lst[now]].r].f += mxflow;
				now = pre[now];
			}
			ans += mxcost;
			printf("%lld ", ans);
		}
		puts("");


		for (int i = m + 1; i <= n + m + 1; i++) {
			for (auto it : G[i]) {
				int v = it.v;
				if (v >= 1 && v <= m && it.f) {
					printf("%d ", b[v]);
					break;
				}
			}
			if (i == n + m + 1)
				puts("");
			else
				printf("%d ", a[i - m]);
		}
	}
	return 0;  // 改数组大小!!!用pair记得改宏定义!!!
}

 

你可能感兴趣的:(网络流,日常训练)