P7730 [JDWOI-1] 蜀道难

nn 座山,每座山高度为 hih_i

mm 种操作,每一次操作可以把连续 lil_i 座山的高度抬高或压低 11,费用为 cic_i

问,让 nn 座山的高度不下降,最少需多少费用?

如果无解,输出 -1

1n,m200,1lin,1hi,ci1031 \leq n,m\leq 200,1 \leq l_i\leq n,1 \leq h_i,c_i \leq 10^3,时限 1s1\text{s},空限 128MB128\text{MB}

std

「蜀道难,难于上青天」。

考虑到,当 nn 座山的高度不下降时,有:i(1,n],aiai1\forall i \in(1,n],a_i \geq a_{i-1},即 i(1,n],aiai10\forall i \in(1,n],a_i-a_{i-1} \geq 0

但是这里修改一个数的话,对它后面的数都有影响,不太好弄。

考虑差分维护,记 di=aiai1d_i=a_i-a_{i-1},(这时点之间就互不影响了)。

每次对区间 [l,r][l,r] 的操作即 dl+1,dr+11d_l+1,d_{r+1}-1(加) 或 dl1,dr+1+1d_l-1,d_{r+1}+1(减)。

那么我们要让 i[1,n],di0\forall i \in[1,n],d_i \geq 0

考虑到,我们不会让某一座山先抬高再压低或先压低抬高再抬高。

那么对于 di>0d_i > 0,最多被减 did_i 次,即最多为其他点提供 did_i 次加;di<0d_i <0,至少需要被加 did_i 次。

总结一下,模型如下:

  • 如果 di>0d_i >0,连一条从 ssii,容量为 did_i,费用为 00 的边(表示这个点有 did_i 次给其他点加的机会)。
  • 如果 di0d_i \leq 0,连一条从 iitt,容量为 di-d_i,费用为 00 的边(表示这个点需要被加 did_i 次)。
  • 对于区间 [l,r][l,r] 的加法操作,连一条从 r+1r+1ll,容量为 infinf,费用为 cic_i 的边(表示加机会的转移)。
  • 对于区间 [l,r][l,r] 的减法操作,连一条从 llr+1r+1,容量为 infinf,费用为 cic_i 的边(表示加机会的转移)。

最后就是跑最小费用最大流了。

记最大流为 gg,则无解的情况即为 g<i=1n[di<0]dig < \sum\limits_{i=1}^{n}[d_i<0]-d_i

#include <bits/stdc++.h>

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

inline void write(int x)
{
	if (x < 0)
	{
		putchar('-');
		x = -x;
	}
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
}

typedef int tp;

const int _ = 6e5 + 10, inf = 947483647;

int n, m, a[_], b[_], s, t, lv[_], cur[_];

tp maxflow, mincost;

int tot = 1, head[_], to[_ << 1], nxt[_ << 1];

tp dis[_], w[_ << 1], fl[_ << 1];

int mc[2][_];

inline void add(int u, int v, tp dis, tp c)
{
	to[++tot] = v;
	nxt[tot] = head[u];
	fl[tot] = dis;
	w[tot] = c;
	head[u] = tot;
}

inline void Add(int u, int v, tp dis, tp c)
{
	add(u, v, dis, c);
	add(v, u, 0, -c);
}

inline bool bfs()
{
	memset(lv, 0, sizeof(lv));
	for (int i = 0; i < _; ++i)
		dis[i] = inf;
	lv[s] = 1;
	dis[s] = 0;
	memcpy(cur, head, sizeof(head));
	queue<int> q;
	q.push(s);
	while (!q.empty())
	{
		int p = q.front();
		q.pop();
		lv[p] = 0;
		for (int eg = head[p]; eg; eg = nxt[eg])
		{
			int v = to[eg];
			tp vol = fl[eg];
			if (vol > 0 && dis[v] > dis[p] + w[eg])
			{
				dis[v] = dis[p] + w[eg];
				if (!lv[v])
				{
					q.push(v);
					lv[v] = 1;
				}
			}
		}
	}
	return dis[t] != inf;
}

tp dfs(int p = s, tp flow = inf)
{
	if (p == t)
		return flow;
	lv[p] = 1;
	tp rmn = flow;
	for (int eg = cur[p]; eg && rmn; eg = nxt[eg])
	{
		cur[p] = eg;
		int v = to[eg];
		tp vol = fl[eg];
		if (vol > 0 && !lv[v] && dis[v] == dis[p] + w[eg])
		{
			tp c = dfs(v, min(vol, rmn));
			rmn -= c;
			fl[eg] -= c;
			fl[eg ^ 1] += c;
		}
	}
	lv[p] = 0;
	return flow - rmn;
}

inline void dinic()
{
	while (bfs())
	{
		tp flow = dfs();
		maxflow += flow;
		mincost += dis[t] * flow;
	}
}

int d1[_], d2[_];

signed main()
{
	char op;
	int x, y, sum = 0;
	n = read(), m = read();
	s = 0, t = _ - 1;
	for (int i = 1; i <= n; ++i)
	{
		b[i] = read();
		a[i] = b[i] - b[i - 1];
	}
	for (int i = 0; i <= n + 1; ++i)
		d1[i] = d2[i] = inf;
	for (int i = 1; i <= m; ++i)
	{
		cin >> op;
		x = read(), y = read();
		if (op == '+')
			d1[x] = min(d1[x], y);
		else
			d2[x] = min(d2[x], y);
	}
	Add(s, n + 1, inf, 0);
	for (int i = 1; i <= n; ++i)
		if (a[i] > 0)
			Add(s, i, a[i], 0);
		else
		{
			Add(i, t, -a[i], 0);
			sum -= a[i];
		}
	for (int i = 1; i <= n; ++i)
		for (int l = 1, r = l + i; r <= n + 1; ++l, r = l + i)
		{
			if (d1[i] < inf)
				Add(r, l, inf, d1[i]);
			if (d2[i] < inf)
				Add(l, r, inf, d2[i]);
		}
	dinic();
	if(maxflow < sum)
	{
		puts("-1");
		return 0;
	}
	write(mincost), putchar('\n');
	return 0;
}
posted @ 2022-02-25 21:02  蒟蒻orz  阅读(1)  评论(0编辑  收藏  举报  来源