713C
费用流
并没有想出来构图方法 我们设立源汇,其实我们关心的是相邻两个值的差值,如果差值小于0说明需要长高,那么向汇点连边差值,说明需要修改,如果差大于零,那么由源点连边差值,说明可以提供修改空间,再由源点向1和n+1连边inf,因为这两个点是可以无限修改的。然后1-2-3-n+1连双向边,费用为1,容量inf,表明修改差值。正向流是提高后面的值,反向流是降低前面的值。而且得加堆优化迪杰斯特拉,否则跑不过去了。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll, int> PII; const int N = 3010; const ll inf = 100000000000010; struct edge { int nxt, to; ll f, c; } e[N * 20]; int inq[N], a[N], b[N], head[N], pree[N], pprev[N]; ll dis[N]; priority_queue<PII, vector<PII>, greater<PII> > q; int n, source, sink, cnt = 1; inline void link(int u, int v, ll f, ll c) { e[++cnt].nxt = head[u]; head[u] = cnt; e[cnt].to = v; e[cnt].f = f; e[cnt].c = c; } inline void insert(int u, int v, ll f, ll c) { link(u, v, f, c); link(v, u, 0, -c); } inline bool spfa() { int l = 1, r = 0; for(int i = source; i <= sink; ++i) dis[i] = inf; dis[source] = 0; q.push(make_pair(0, source)); while(!q.empty()) { PII x = q.top(); q.pop(); int u = x.second; if(dis[u] != x.first) continue; for(int i = head[u]; i; i = e[i].nxt) if(e[i].f && dis[e[i].to] > dis[u] + e[i].c) { pree[e[i].to] = i; pprev[e[i].to] = u; dis[e[i].to] = dis[u] + e[i].c; q.push(make_pair(dis[e[i].to], e[i].to)); } } return dis[sink] != inf; } inline ll getflow() { int now = sink; ll delta = inf; while(now != source) { delta = min(delta, e[pree[now]].f); now = pprev[now]; } now = sink; while(now != source) { e[pree[now]].f -= delta; e[pree[now] ^ 1].f += delta; now = pprev[now]; } return delta * dis[sink]; } inline ll mcmf() { ll ret = 0; while(spfa()) ret += getflow(); return ret; } int main() { scanf("%d", &n); source = 0; sink = n + 2; for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); for(int i = 2; i <= n; ++i) { b[i] = a[i] - a[i - 1]; if(b[i] > 0) insert(i, sink, b[i] - 1, 0); else insert(source, i, -b[i] + 1, 0); } for(int i = 1; i <= n; ++i) { insert(i, i + 1, inf, 1); insert(i + 1, i, inf, 1); } insert(1, sink, inf, 0); insert(n + 1, sink, inf, 0); printf("%lld\n", mcmf()); return 0; }