[AT2558] [ARC073 F] Many Moves

43 篇文章 0 订阅
19 篇文章 0 订阅
洛谷传送门
Atcoder传送门

题目大意

在一行中有 n n n个格子,从左往右编号为 1 1 1 n n n

2 2 2颗棋子,一开始分别位于位置 A A A B B B。按顺序给出 Q Q Q个要求,每个要求是如下形式:

  • 给出一个位置 x i x_i xi,要求将两个棋子中任意一个移动到位置 x i x_i xi

将一颗棋子移动一格需要花费 1 1 1秒,就是说将棋子从 X X X位置移动到 Y Y Y位置需要花费 ∣ X − Y ∣ |X-Y| XY秒。

为了回答要求,你只能移动棋子,并且同一时刻只能移动一颗棋子。要求的顺序是不可更改的。在同一时间允许两颗棋子在同一个格子内。

输入输出格式

输入格式

第一行 4 4 4个整数,分别为 n , Q , A , B n,Q,A,B n,Q,A,B

第二行 Q Q Q个整数,第 i i i个整数为 x i x_i xi

  • 1 ≤ n , Q ≤ 2 × 1 0 5 1\leq n,Q\leq 2\times 10^5 1n,Q2×105
  • 1 ≤ A , B ≤ n 1\leq A,B\leq n 1A,Bn
  • 1 ≤ x i ≤ n 1\leq x_i\leq n 1xin
输出格式

最小需要多少秒回答全部要求。

输入输出样例

输入样例#1:
8 3 1 8
3 5 1
输出样例#1:
7
输入样例#2:
9 2 1 9
5 1
输出样例#2:
4
输入样例#3:
9 2 1 9
5 9
输出样例#3:
4
输入样例#4:
11 16 8 1
1 1 5 1 11 4 5 2 5 3 3 3 5 5 6 7
输出样例#4:
21
输入样例#5:
8 3 1 8
3 5 1
输出样例#5:
7
输入样例#6:
9 2 1 9
5 1
输出样例#6:
4
输入样例#7:
9 2 1 9
5 9
输出样例#7:
4
输入样例#8:
11 16 8 1
1 1 5 1 11 4 5 2 5 3 3 3 5 5 6 7
输出样例#8:
21

解题分析

很妙妙暴力的一道题。 首先因为每次操作后一个棋子的位置是固定的, 我们有个很暴力的DP: 设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i个操作后另外一枚棋子在 j j j时的最小代价, 那么就有:
d p [ i ] [ x i − 1 ] = m i n { d p [ i − 1 ] [ j ] + ∣ j − x i ∣ } d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + ∣ x i − x i − 1 ∣ ( j ≠ x i − 1 ) dp[i][x_{i-1}]=min\{dp[i-1][j]+|j-x_i|\} \\ dp[i][j]=dp[i-1][j]+|x_i-x_{i-1}|(j\ne x_{i-1}) dp[i][xi1]=min{dp[i1][j]+jxi}dp[i][j]=dp[i1][j]+xixi1(j̸=xi1)
下面那个就是个区间加操作, 上面那个把绝对值分类讨论了就是一个区间最小值的问题, 直接线段树维护。

代码如下:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <iostream>
#include <cstring>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ls (now << 1)
#define rs ((now << 1) | 1)
#define MX 200500
#define gc getchar()
#define ll long long
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;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
template <class T> IN T abs(T a) {return a > 0 ? a : -a;}
int n, q;
struct Node
{
	ll val[3], tag;
	Node(){std::memset(val, 63, sizeof(val));}
}tree[MX << 2];
IN void pushup(R int now)
{
	tree[now].val[0] = min(tree[ls].val[0], tree[rs].val[0]);
	tree[now].val[1] = min(tree[ls].val[1], tree[rs].val[1]);
	tree[now].val[2] = min(tree[ls].val[2], tree[rs].val[2]);
}
IN void pushdown(R int now)
{
	if (tree[now].tag)
	{
		tree[ls].val[0] += tree[now].tag;
		tree[ls].val[1] += tree[now].tag;
		tree[ls].val[2] += tree[now].tag;
		tree[ls].tag	+= tree[now].tag;
		tree[rs].val[0] += tree[now].tag;
		tree[rs].val[1] += tree[now].tag;
		tree[rs].val[2] += tree[now].tag;
		tree[rs].tag	+= tree[now].tag;
		tree[now].tag = 0;
	}
}
void modify(R int now, R int lef, R int rig, R int pos, R ll v)
{
	if (lef == rig)
	{
		tree[now].val[0] = v;
		tree[now].val[1] = v - pos;
		tree[now].val[2] = v + pos;
		return;
	}
	pushdown(now);
	int mid = lef + rig >> 1;
	if (pos <= mid) modify(ls, lef, mid, pos, v);
	else modify(rs, mid + 1, rig, pos, v);
	pushup(now);
}
IN void add(R ll v)
{
	tree[1].tag += v;
	tree[1].val[0] += v;
	tree[1].val[1] += v;
	tree[1].val[2] += v;
}
ll query(R int now, R int lef, R int rig, R int lb, R int rb, R int id)
{
	if (lef >= lb && rig <= rb) return tree[now].val[id];
	pushdown(now);
	int mid = lef + rig >> 1; ll ret = LONG_LONG_MAX;
	if (lb <= mid) ret = query(ls, lef, mid, lb, rb, id);
	if (rb > mid) ret = min(ret, query(rs, mid + 1, rig, lb, rb, id));
	return ret;
}
int main(void)
{
	int a, b, last, cur, del;
	ll res1, res2, res3;
	in(n), in(q), in(a), in(b);
	in(last);
	modify(1, 1, n, b, abs(last - a));
	modify(1, 1, n, a, abs(last - b));
	W (--q)
	{
		in(cur); del = abs(last - cur);
		res1 = query(1, 1, n, cur, cur, 0) + del;
		res2 = query(1, 1, n, 1, cur, 1) + cur;
		res3 = query(1, 1, n, cur + 1, n, 2) - cur;
		res1 = min(res1, min(res2, res3));
		add(del);
		modify(1, 1, n, last, res1);
		last = cur;
	}
	printf("%lld", tree[1].val[0]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值