bzoj 4873 [Shoi2017]寿司餐厅 最大权闭合子图

题面

题目传送门

解法

  • 发现每一种寿司的组合方式都只能被计算一次,并且存在一些依赖关系:比如说选择了 [ l , r ] [l,r] 就必须选择它的所有子区间。
  • 这启发我们利用最大权闭合子图来解决问题。不妨考虑将所有区间看成点,按照最大权闭合子图的方式来建图。当然,对于区间 [ l , r ] [l,r] 我们不一定要连向所有的子区间,只要连向 [ l , r 1 ] [l,r-1] [ l + 1 , r ] [l+1,r] 就可以了。
  • 然后考虑如何处理 m x 2 + c x mx^2+cx 的情况。可以对于每一种 x x 建出一个点,然后将所有 a [ i ] = x a[i]=x 的点 [ i , i ] [i,i] 连向 x x ,然后 x x T T 连一条容量为 m x 2 mx^2 的边,这就处理完了 m x 2 mx^2 的问题。然后处理 c x cx ,只要将所有 a [ i ] = x a[i]=x 的点向 T T 连一条容量为 x x 的边就可以了。
  • 考虑到这是一个最小割问题,所以算法显然是正确的。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 110, M = N * N, inf = 1 << 30;
int cnt, a[N], b[N], d[N][N], num[N][N], l[M], cur[M], head[M];
struct Edge {int next, num, c;} e[N * N * N];
void add(int x, int y, int c) {e[++cnt] = (Edge) {head[x], y, c}, head[x] = cnt;}
void Add(int x, int y, int c) {add(x, y, c), add(y, x, 0);}
bool bfs(int s, int t) {
	for (int i = s; i <= t; i++) l[i] = -1;
	queue <int> q; q.push(s), l[s] = 0;
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int p = head[x]; p; p = e[p].next) {
			int y = e[p].num, c = e[p].c;
			if (c && l[y] == -1) q.push(y), l[y] = l[x] + 1;
		}
	}
	return l[t] != -1;
}
int dfs(int x, int t, int lim) {
	if (x == t) return lim; int ret = 0;
	for (int &p = cur[x]; p; p = e[p].next) {
		int y = e[p].num, c = e[p].c;
		if (l[y] == l[x] + 1 && c) {
			int w = dfs(y, t, min(lim - ret, c));
			e[p].c -= w, e[p ^ 1].c += w, ret += w;
			if (ret == lim) return ret;
		}
	}
	if (!ret) l[x] = -1; return ret;
}
int dinic(int s, int t) {
	int ret = 0;
	while (bfs(s, t)) {
		memcpy(cur, head, sizeof(cur));
		ret += dfs(s, t, inf);
	}
	return ret;
}
int main() {
	int n, m, tot = 0; read(n), read(m); cnt = 1;
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1; i <= n; i++)
		for (int j = i; j <= n; j++)
			read(d[i][j]), num[i][j] = ++tot;
	int s = 0, t = n + tot + 1; ll ans = 0;
	for (int i = 1; i <= n; i++)
		for (int j = i; j <= n; j++) {
			if (d[i][j] > 0) ans += d[i][j], Add(s, num[i][j], d[i][j]);
				else Add(num[i][j], t, -d[i][j]);
			if (i < j) Add(num[i][j], num[i][j - 1], inf), Add(num[i][j], num[i + 1][j], inf);
		}
	map <int, int> h; h.clear();
	for (int i = 1; i <= n; i++)
		if (h.count(a[i])) b[i] = h[a[i]];
			else b[i] = h[a[i]] = ++tot, Add(tot, t, m * a[i] * a[i]);
	for (int i = 1; i <= n; i++) Add(num[i][i], b[i], inf), Add(num[i][i], t, a[i]);
	cout << ans - dinic(s, t) << "\n";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/88674622