[BZOJ 5222] [Lydsy2017省队十连测]怪题

BZOJ传送门

题目描述

给出一个长度为 n n n的整数序列 h i h_i hi,现在要通过一些操作将这个序列修改为单调不降序列,即 h i ≤ h i + 1 h_i≤h_i+1 hihi+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,m5hi10ci3。对于另20%的数据, l i = 1 , h i ≤ 500 l_i=1,h_i≤500 li=1hi500。对于100%的数据, n , m ≤ 200 n,m≤200 n,m200 l i ≤ n l_i≤n lin 1 ≤ h i , c i ≤ 1 0 6 1≤h_i,c_i≤10^6 1hi,ci106

解题分析

应该很容易看出来如果我们维护数列的差分数列, 那么每个操作就相当于区间的一个端点 − 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();
}

### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值