题目链接
解析
\(dalao\)一看这题就知道是个最大权闭合子图
然而蒟蒻我看了题解才知道
然后一搜才知道以前写过的某些最小割叫最大权闭合子图,原来是有套路的……
首先观察题目,发现如果收益中有\(d_{i,j}\),那么也一定有\(d_{i - 1, j}\)和\(d_{i, j - 1}\),因为每次选择只能是连续区间
我们给每个区间\([i, j]\)建一个点,它的权值为\(d_{i, j}\),并从它向\([i + 1, j]\),\([i, j - 1]\)两个点连边,表示选了\([i, j]\)就必须选\([i + 1, j]\)和\([i, j - 1]\)
每个代号\(x\)的花费可以拆成两部分:\(mx^2\)和\(cx\)
前一部分只和这个代号选没选有关,所以每个代号建一个点,权值为\(-mx^2\),每个\([i, i]\)向它的代号\(a[i]\)连边,表示选了\(i\)就必选它的代号
后一部分和选的寿司种类有关,假设选了\([i, i]\),那么会增加\(a[i]\)的花费,所以可以把\([i, i]\)这个点的权值减去\(a[i]\)
上面建出的是原图,然后按最大权闭合子图的套路转化成最小割即可,这里就不写怎么转化了……
P.S.
题面巨长,变量的关系需要好好捋一捋……
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define id(x, y) (((x) - 1) * N + (y))
typedef long long LL;
const int inf = 0x3f3f3f3f;
struct Graph {
struct Edge {
int v, next, cap;
Edge(int _v = 0, int _n = 0, int _c = 0):v(_v), next(_n), cap(_c) {}
};
std::vector<Edge> edge;
int cnt, head[11500], cur[11500], dep[11500];
void init() { memset(head, -1, sizeof head); cnt = 0; }
void add_edge(int u, int v, int c) { edge.push_back(Edge(v, head[u], c)); head[u] = edge.size() - 1; }
void insert(int u, int v, int c) { add_edge(u, v, c); add_edge(v, u, 0); }
bool bfs();
int dfs(int, int);
int Dinic();
} G;
int a[105], d[105][105];
int N, M, ans;
int main() {
G.init();
std::ios::sync_with_stdio(false);
std::cin >> N >> M;
for (int i = 1; i <= N; ++i) std::cin >> a[i];
for (int i = 1; i <= N; ++i) for (int j = i; j <= N; ++j) std::cin >> d[i][j];
for (int i = 1; i <= N; ++i) for (int j = i + 1; j <= N; ++j) { G.insert(id(i, j), id(i, j - 1), inf); G.insert(id(i, j), id(i + 1, j), inf); }
for (int i = 1; i <= N; ++i) G.insert(id(i, i), id(N, N) + a[i], inf);
for (int i = 1; i <= 1000; ++i) G.insert(id(N, N) + i, id(N, N) + 1001, M * i * i);
for (int i = 1; i <= N; ++i) G.insert(id(i, i), id(N, N) + 1001, a[i]);
for (int i = 1; i <= N; ++i) for (int j = i; j <= N; ++j)
if (d[i][j] > 0) G.insert(0, id(i, j), d[i][j]), ans += d[i][j];
else if (d[i][j] < 0) G.insert(id(i, j), id(N, N) + 1001, -d[i][j]);
std::cout << ans - G.Dinic() << std::endl;
return 0;
}
bool Graph::bfs() {
int que[11500], hd = 0, tl = 1;
memset(dep, -1, sizeof dep);
que[0] = dep[0] = 0;
while (hd < tl) {
int p = que[hd++];
for (int i = head[p]; ~i; i = edge[i].next)
if (edge[i].cap && !(~dep[edge[i].v])) {
dep[edge[i].v] = dep[p] + 1;
que[tl++] = edge[i].v;
}
}
return ~dep[id(N, N) + 1001];
}
int Graph::dfs(int u, int max_flow) {
if (u == id(N, N) + 1001) return max_flow;
int res = 0;
for (int &i = cur[u]; ~i; i = edge[i].next)
if (edge[i].cap && dep[edge[i].v] == dep[u] + 1) {
int d = dfs(edge[i].v, std::min(edge[i].cap, max_flow - res));
res += d, edge[i].cap -= d, edge[i ^ 1].cap += d;
if (res == max_flow) break;
}
if (!res) dep[u] = -1;
return res;
}
int Graph::Dinic() {
int res = 0;
while (bfs()) {
memcpy(cur, head, sizeof head);
res += dfs(0, inf);
}
//std::cout << res << std::endl;
return res;
}
//Rhein_E