【YBT2023寒假Day1 A】孤走暗巷(费用流)
孤走暗巷
题目链接:YBT2023寒假Day1 A
题目大意
给你一个整数序列,你要通过一些操作把它变成单调不降序列。
你有 m 种操作,每次可以选择一个长度为 li 的区间,花费 ci 的代价将其加一或减一(是加一还是减一看给你的操作),问你最小代价,或者输出无解。
思路
首先这个区间加减不难想到通过差分变成某两个位置分别加一减一。
然后发现变成单调不降就是中间的部分都是要非 \(0\) 数。
看到 \(n,m\) 都很小,而且 DP 似乎不太可行(因为有两个位置同时修改),考虑费用流。
然后因为两个位置同时修改的都是 \(1\) 的绝对值,而且是相反数,你可以理解为把这个 \(1\) 交给了另一个位置。
那你在思考一开始的正负,会发现正的就是它可以给出的最大数量,负的就是它需要的最小数量。
然后因为两边的正负是无所谓的,也就是它可以给出无限的数量。
那我们就把正的跟源点连,负的跟汇点连,两边就是跟源点连流量无限大,都是费用 \(0\)。
然后再模拟给的操作思考会发现是给的流向收到的,然后连边流量无限费用是给出的值。
不过要注意的是它只是限制的区间的长度,所以你可以直接从左往右滑动那个区间,每个都要连边,边数是 \(O(nm)\) 级别没问题。
至于判断是否有解,其实就是看是否刘曼,然后因为源点那边有无限流量的边,我们判断汇点那边的边就行。
代码
#include<queue>
#include<cstdio>
#include<iostream>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 250;
const int M = N * N * 20;
const int inf = 1e8;
struct node {
ll x, w, to, nxt, op;
}e[M];
ll n, m, S, T, tot, le[M], lee[M], KK;
ll dis[M], deg[M], h[N], p[N];
bool in[M];
queue <ll> q;
void add(ll x, ll y, ll z, ll w) {
e[++KK] = (node){z, w, y, le[x], KK + 1}; le[x] = KK;
e[++KK] = (node){0, -w, x, le[y], KK - 1}; le[y] = KK;
}
bool SPFA() {
for (ll i = 0; i <= tot; i++) {
dis[i] = INF;
in[i] = 0;
deg[i] = -1;
lee[i] = le[i];
}
while (!q.empty()) q.pop();
q.push(S);
dis[S] = 0;
in[S] = 1;
deg[S] = 0;
while (!q.empty()) {
ll now = q.front();
q.pop();
for (ll i = le[now]; i; i = e[i].nxt)
if (e[i].x > 0 && dis[e[i].to] > dis[now] + e[i].w) {
dis[e[i].to] = dis[now] + e[i].w;
deg[e[i].to] = deg[now] + 1;
if (!in[e[i].to]) {
in[e[i].to] = 1;
q.push(e[i].to);
}
}
in[now] = 0;
}
if (dis[T] == dis[0]) return 0;
return 1;
}
ll dfs(ll now, ll sum) {
if (now == T) return sum;
in[now] = 1;
ll go = 0;
for (ll &i = lee[now]; i; i = e[i].nxt)
if (!in[e[i].to] && e[i].x > 0 && dis[e[i].to] == dis[now] + e[i].w && deg[e[i].to] == deg[now] + 1) {
ll this_go = dfs(e[i].to, min(sum - go, e[i].x));
if (this_go) {
e[i].x -= this_go;
e[e[i].op].x += this_go;
go += this_go;
if (go == sum) return go;
}
}
if (go == sum) in[now] = 0;
return go;
}
ll dinic_cost() {
ll re = 0;
while (SPFA()) {
re += dis[T] * dfs(S, INF);
}
return re;
}
int main() {
freopen("seq.in", "r", stdin);
freopen("seq.out", "w", stdout);
scanf("%lld %lld", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld", &h[i]);
for (int i = 1; i <= n + 1; i++) p[i] = h[i] - h[i - 1];
S = n + 2;
T = n + 3;
tot = T;
for (int i = 1; i <= n + 1; i++) {
if (i == 1 || i == n + 1) add(S, i, inf, 0);
else if (p[i] > 0) add(S, i, p[i], 0);
else if (p[i] < 0) add(i, T, -p[i], 0);
}
for (int i = 1; i <= m; i++) {
char op = getchar(); while (op != '+' && op != '-') op = getchar();
int l, c; scanf("%d %d", &l, &c); if (l == n) continue;
for (int j = 1; j + l <= n + 1; j++) {
if (op == '-') add(j, j + l, inf, c);
else add(j + l, j, inf, c);
}
}
ll ans = dinic_cost();
for (int i = le[T]; i; i = e[i].nxt) {
if (e[e[i].op].x) {
printf("-1"); return 0;
}
}
printf("%lld", ans);
return 0;
}