[Luogu 2259] [Oibh]新型计算机

BZOJ传送门

题目描述

Tim正在摆弄着他设计的“计算机”,他认为这台计算机原理很独特,因此利用它可以解决许多难题。  但是,有一个难题他却解决不了,是这台计算机的输入问题。新型计算机的输入也很独特,假设输入序列中有一些数字(都是自然数——自然数包括 0 0 0),计算机先读取第一个数字 S 1 S_1 S1,然后顺序向后读入 S 1 S_1 S1个数字。接着再读一个数字 S 2 S_2 S2,顺序向后读入 S 2 S_2 S2个数字……依此类推。不过只有计算机正好将输入序列中的数字读完,它才能正确处理数据,否则计算机就会进行自毁性操作!  Tim现在有一串输入序列。但可能不是合法的,也就是可能会对计算机造成破坏。于是他想对序列中的每一个数字做一些更改,加上一个数或者减去一个数,当然,仍然保持其为自然数。使得更改后的序列为一个新型计算机可以接受的合法序列。  不过Tim还希望更改的总代价最小,所谓总代价,就是对序列中每一个数操作的参数的绝对值之和。

写一个程序:   从文件中读入原始的输入序列;   计算将输入序列改变为合法序列需要的最小代价;   向输出文件打印结果。

输入输出格式

输入格式

输入文件包含两行,第一行一个正整数 N N N N &lt; 1000001 N&lt;1 000 001 N<1000001。  输入文件第二行包含 N N N个自然数,表示输入序列。

输出格式

仅一个整数,表示把输入序列改变为合法序列需要的最小代价,保证最小代价小于 1 0 9 10^9 109

输入输出样例

输入样例#1:
4
2 2 2 2
输出样例#1:
1

解题分析

如果我们把每个点以及其可以到达点连起来, 我们可以得到一个DAG。 更改数字可以等价于:我们可以花费 1 1 1的代价向后走一步, 或向前走一步。 这样我们就建立了一个最短路模型, 暴力连边最短路即可。

注意我们必须要先走一条原来的边才能走边长为1的边, 所以边有一个左边界, 连边的时候应该注意。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <ext/pb_ds/priority_queue.hpp>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1005000
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
struct Pass {int pos, dis;};
IN bool operator < (const Pass &x, const Pass &y)
{return x.dis > y.dis;}
__gnu_pbds::priority_queue <Pass> q;
__gnu_pbds::priority_queue <Pass>::point_iterator hdle[MX];
int head[MX], dis[MX], pre[MX], nex[MX];
bool vis[MX];
int dot, cnt;
struct Edge {int to, len, nex;} edge[MX << 3];
IN void add(R int from, R int to, R int val)
{edge[++cnt] = {to, val, head[from]}, head[from] = cnt;}
void dj(R int st)
{
	std::memset(dis, 63, sizeof(dis));
	q.push({st, 0}), dis[st] = 0;
	Pass cur; R int now, i;
	W (!q.empty())
	{
		cur = q.top(); q.pop(); now = cur.pos;
		for (i = head[now]; i; i = edge[i].nex)
		{
			if(dis[edge[i].to] > dis[now] + edge[i].len)
			{
				dis[edge[i].to] = dis[now] + edge[i].len;
				if(!vis[edge[i].to])
				hdle[edge[i].to] = q.push({edge[i].to, dis[edge[i].to]}), vis[edge[i].to] = true;
				else q.modify(hdle[edge[i].to], {edge[i].to, dis[now] + edge[i].len});
			}
		}
	}
	printf("%d", dis[dot + 1]);
}
int main(void)
{
	int buf, bd; in(dot); int r = dot;
	for (R int i = 1; i <= dot; ++i)
	{
		in(buf);
		if(i + buf <= dot) add(i, i + buf + 1, 0), r = std::min(r, i + buf + 1);
		else add(i, dot + 1, i + buf - dot);
	}
	for (R int i = r; i <= dot; ++i) add(i, i + 1, 1), add(i, i - 1, 1);
	dj(1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值