BZOJ传送门
题目描述
给出一个长度为 n n n的整数序列 h i h_i hi,现在要通过一些操作将这个序列修改为单调不降序列,即 h i ≤ h i + 1 h_i≤h_i+1 hi≤hi+1。
可以用的操作有 m m m种,第 i i i种操作可以通过支付 c i c_i ci的代价将一段长度恰为 l i l_i li的连续子序列 + 1 +1 +1或 − 1 −1 −1(由对应的操作符确定是 + 1 +1 +1还是 − 1 −1 −1,具体参考 输入格式) 。
不限制每种操作的使用次数,序列中的 h i h_i hi可以被改为任意整数(可以是负数),求最小代价,无解输出 − 1 −1 −1。
输入输出格式
输入格式
第一行,两个整数 n , m n,m n,m。
第二行, n n n个整数 h i h_i hi。
接下来 m m m行,每行格式为 o p i , l i , c i op_i,l_i,c_i opi,li,ci空格隔开,其中 o p i op_i opi为一个字符,表示这种操作是 + 1 +1 +1还是 − 1 −1 −1。
输出格式
输出一行一个整数表示最小代价,若无解输出 − 1 −1 −1。
输入输出样例
输入样例#1:
3 2
3 2 1
+ 1 1
- 1 1
输出样例#1:
2
输入样例#2:
3 1
3 2 1
+ 2 1
输出样例#2:
-1
输入样例#3:
10 10
23 1 8 14 2 3 15 50 53 53
+ 4 6
- 1 10
+ 2 4
+ 4 2
- 3 5
+ 1 2
+ 3 2
+ 5 7
- 1 6
+ 4 5
输出样例#3
96
数据规模
对于20%的数据, n , m ≤ 5 , h i ≤ 10 , c i ≤ 3 n,m≤5,h_i≤10,c_i≤3 n,m≤5,hi≤10,ci≤3。对于另20%的数据, l i = 1 , h i ≤ 500 l_i=1,h_i≤500 li=1,hi≤500。对于100%的数据, n , m ≤ 200 n,m≤200 n,m≤200, l i ≤ n l_i≤n li≤n, 1 ≤ h i , c i ≤ 1 0 6 1≤h_i,c_i≤10^6 1≤hi,ci≤106。
解题分析
应该很容易看出来如果我们维护数列的差分数列, 那么每个操作就相当于区间的一个端点 − 1 -1 −1, 一个端点 + 1 +1 +1, 那么就相当于从 + + +的点向 − - −的点连流量为 I N F INF INF, 花费为 c i c_i ci的边, 暴力 O ( N 2 ) O(N^2) O(N2)连边即可。然后我们再利用差分的高度限流, 最后流满即为有解。 注意这个差分数列的最后一项我们不必满足操作后都要 ≥ 0 \ge0 ≥0的条件, 所以源点连向它的流量是 I N F INF INF, 同理我们也不需要保证第一项, 所以源点连向第一个点的流量也是 I N F INF INF。
复杂度 O ( ? ? ? ) O(???) O(???), 反正跑的贼快…
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <queue>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define File freopen("seq.in", "r", stdin), freopen("seq.out", "w", stdout)
#define MX 405
#define INF 2000000000
#define S 402
#define T 403
#define ll long long
template <class TT>
IN void in(TT &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int cnt, dot, kind, tar, tot;
ll ans;
int head[MX], pre[MX], dis[MX], del[MX], h[MX], dat[MX];
bool vis[MX];
struct Edge {int to, cost, fl, nex;} edge[MX * MX * 10];
IN void add(R int from, R int to, R int fl, R int cost)
{
edge[++cnt] = {to, cost, fl, head[from]}, head[from] = cnt;
edge[++cnt] = {from, -cost, 0, head[to]}, head[to] = cnt;
}
namespace MCMF
{
std::queue <int> q;
IN bool SPFA()
{
std::memset(dis, 127, sizeof(dis));
dis[S] = 0; del[S] = INF; q.push(S); R int now;
W (!q.empty())
{
now = q.front(); q.pop();
for (R int i = head[now]; ~i; i = edge[i].nex)
{
if(edge[i].fl && dis[edge[i].to] > dis[now] + edge[i].cost)
{
dis[edge[i].to] = dis[now] + edge[i].cost;
del[edge[i].to] = std::min(del[now], edge[i].fl);
pre[edge[i].to] = i;
if(!vis[edge[i].to]) vis[edge[i].to] = true, q.push(edge[i].to);
}
}
vis[now] = false;
}
return dis[T] < INF;
}
IN void updata()
{
tot += del[T], ans += 1ll * del[T] * dis[T];
R int now = T, pr;
W (now != S)
{
pr = pre[now];
edge[pr].fl -= del[T];
edge[pr ^ 1].fl += del[T];
now = edge[pr ^ 1].to;
}
}
void init()
{
W (SPFA()) updata();
if(tot == tar) printf("%lld", ans);
else printf("-1");
}
}
signed main(void)
{
File;
char buf[10]; int a, b;
std::memset(head, cnt = -1, sizeof(head));
in(dot), in(kind);
for (R int i = 1; i <= dot; ++i)
{
in(h[i]), dat[i] = h[i] - h[i - 1];
if(dat[i] < 0) add(i, T, -dat[i], 0), tar -= dat[i];
else add(S, i, dat[i], 0);
} add(S, dot + 1, INF, 0); add(S, 1, INF, 0);
for (R int i = 1; i <= kind; ++i)
{
scanf("%s", buf), in(a), in(b);
if(buf[0] == '+')
{
for (R int j = dot + 1; j > a; --j)
add(j, j - a, INF, b);
}
else
{
for (R int j = dot + 1; j > a; --j)
add(j - a, j, INF, b);
}
}
MCMF::init();
}