【arc073D】Many Moves
Description
有\(n\)个格子,编号从左到右为\(1\sim n\),一开始有两个棋子,位置给定,接下来要依次进行\(Q\)次操作,第\(i\)次操作必须选择一个棋子将其移动到\(x_i\)上面(\(x\)数组给定),所需代价是当前位置与目标位置的编号之差绝对值,允许两个棋子在同一个位置,求完成操作的最小代价
Solution
大家好我是一个不会算复杂度的弱智选手
这题的话。。没有什么想法那就快乐dp咯。。但是状态的设置比较重要,注意到两个棋子其实本质上没有任何区别,我们希望用一种简明的方式将两个棋子的位置都记录下来,然后再注意到第\(i\)次操作完后一定有一个棋子在\(x_i\),那么我们可以用\(f[i][j]\)表示第\(i\)次操作完之后,不在\(x_i\)位置上的那个棋子在\(j\)这个位置,所用的最小代价
那么可以写出转移:
f[i-1][j]+|x_i-x_{i-1}|&(j\neq x_{i-1})\\
\\
min(f[i-1][j]+|x_i-x_{i-1}|,min(f[i-1][k]+|k-x_i|))&(j=x_{i-1})\\
\end{cases}
\]
其中\(k\)的枚举范围是\(1\sim n\)
现在最大的问题就是。。第二种情况,然而注意到比较开心的事情是这种情况只会在一个特定的位置出现所以我们可以考虑用某些数据结构得到这个\(min(f[i-1][k]+|k-x_i|)\)
那当然是用线段树啊,但是这个绝对值有点恶心,但是由于\(x_i\)已知,所以我们可以直接暴力处理一下,对于\(k\in[1,x_i]\)我们询问\(min(f[i-1][k]-k)\),对于\(k\in [x_i,n]\)我们询问\(min(f[i-1][k]+k)\),然后一个\(+x_i\)一个\(-x_i\)再取一下\(min\)就可以了(也就是说线段树分别维护\(f[i]\)、\(f[i]-i\)和\(f[i]+i\)的区间最小值)
至于\(f[i-1][j]+|x_i-x_{i-1}|\)的情况,我们直接区间打标记就好了
注意一下需要开long long
mark:不要看到三个变量就觉得。。优化之后是\(n^2logn\)的。。。
mark:遇到绝对值考虑暴力分两段,如果计算方式不同的话记得分界点两边的都要算
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=200010,inf=2147483647;
int a[N];
namespace Seg{/*{{{*/
const int N=::N*4;
int ch[N][2];
ll mn[N][3],tag[N];//order: - none +
int n,tot;
void _build(int x,int l,int r){
for (int i=0;i<3;++i) mn[x][i]=inf;
if (l==r) return;
int mid=l+r>>1;
ch[x][0]=++tot; _build(ch[x][0],l,mid);
ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
}
void build(int _n){n=_n; tot=1; _build(1,1,n);}
void pushup(int x){
for (int i=0;i<3;++i) mn[x][i]=min(mn[ch[x][0]][i],mn[ch[x][1]][i]);
}
void givetag(int x,ll delta){
tag[x]+=delta;
for (int i=0;i<3;++i) mn[x][i]+=delta;
}
void downtag(int x){
if (!tag[x]) return;
if (ch[x][0]) givetag(ch[x][0],tag[x]);
if (ch[x][1]) givetag(ch[x][1],tag[x]);
tag[x]=0;
}
void _update(int x,int d,int lx,int rx,ll delta){
if (lx==rx){
mn[x][0]=min(mn[x][0],delta-lx);
mn[x][1]=min(mn[x][1],delta);
mn[x][2]=min(mn[x][2],delta+lx);
return;
}
downtag(x);
int mid=lx+rx>>1;
if (d<=mid) _update(ch[x][0],d,lx,mid,delta);
else _update(ch[x][1],d,mid+1,rx,delta);
pushup(x);
}
void update(int d,ll delta){_update(1,d,1,n,delta);}
ll _query(int x,int l,int r,int lx,int rx,int op){
if (l<=lx&&rx<=r) return mn[x][op];
int mid=lx+rx>>1;
downtag(x);
if (r<=mid) return _query(ch[x][0],l,r,lx,mid,op);
else if (l>mid) return _query(ch[x][1],l,r,mid+1,rx,op);
else{
return min(_query(ch[x][0],l,mid,lx,mid,op),_query(ch[x][1],mid+1,r,mid+1,rx,op));
}
pushup(x);
}
ll query(int l,int r,int op){return _query(1,l,r,1,n,op);}
}/*}}}*/
int n,m,st1,st2;
int Abs(int x){return x<0?-x:x;}
void debug(int op){
for (int i=1;i<=n;++i) printf("%lld ",Seg::query(i,i,op));
printf("\n");
}
void dp(){
ll tmp;
Seg::update(st2,0);
//debug(2);
for (int i=1;i<=m;++i){
tmp=min(Seg::query(1,a[i],0)+a[i],Seg::query(a[i],n,2)-a[i]);
Seg::givetag(1,Abs(a[i]-a[i-1]));
Seg::update(a[i-1],tmp);
//debug(1);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d%d%d%d",&n,&m,&st1,&st2);
for (int i=1;i<=m;++i) scanf("%d",a+i);
a[0]=st1;
Seg::build(n);
dp();
printf("%lld\n",Seg::query(1,n,1));
}
【arc073D】Many Moves的更多相关文章
- 【arc073f】Many Moves(动态规划,线段树)
[arc073f]Many Moves(动态规划,线段树) 题面 atcoder 洛谷 题解 设\(f[i][j]\)表示第一个棋子在\(i\),第二个棋子在\(j\)的最小移动代价. 发现在一次移动 ...
- 【bfs】Knight Moves
[题目描述] 输入nn代表有个n×nn×n的棋盘,输入开始位置的坐标和结束位置的坐标,问一个骑士朝棋盘的八个方向走马字步,从开始坐标到结束坐标可以经过多少步. [输入] 首先输入一个nn,表示测试样例 ...
- 【ARC073F】Many Moves
题目 一个显然的\(dp\),设\(dp_{i,j}\)表示其中一个棋子在\(x_i\)点,另一个棋子在\(j\)点的最小花费 显然\(dp_{i,j}\)有两种转移 第一种是把\(x_i\)上的棋子 ...
- 【LeetCode】数学(共106题)
[2]Add Two Numbers (2018年12月23日,review) 链表的高精度加法. 题解:链表专题:https://www.cnblogs.com/zhangwanying/p/979 ...
- 【习题 6-4 UVA-439】Knight Moves
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] bfs模板题 [代码] /* 1.Shoud it use long long ? 2.Have you ever test sev ...
- 【广搜】Knight Moves
题目描述 Mr Somurolov, fabulous chess-gamer indeed, asserts that no one else but him can move knights fr ...
- 1450:【例 3】Knight Moves
1450:[例 3]Knight Moves 题解 这道题可以用双向宽度搜索优化(总介绍在 BFS ) 给定了起始状态和结束状态,求最少步数,显然是用BFS,为了节省时间,选择双向BFS. 双向B ...
- 【模拟】Codeforces 710A King Moves
题目链接: http://codeforces.com/problemset/problem/710/A 题目大意: 国际象棋标准8X8棋盘,国王能往周围8个方向走.输入国王的位置,输出当前国王能往几 ...
- 带给你灵感:30个超棒的 SVG 动画展示【上篇】
前端开发人员和设计师一般使用 CSS 来创建 HTML 元素动画.然而,由于 HTML 在创建图案,形状,和其他方面的局限性,它们自然的转向了 SVG,它提供了更多更有趣的能力.借助SVG,我们有更多 ...
随机推荐
- Linux安装软件包
今天在坐公交的路上看到一遍文章感觉还不错,先收集了.原文链接 Linux安装软件包
- 【Alpha】技术规格说明书
由于第1周已经写过技术规格说明书(设计文档),本周更新了上一版内容. Github地址:https://github.com/buaase/Phylab-Web/blob/master/docs/Ba ...
- 渡过OO的死劫,了解规格的意义——OO第三次博客总结
当熬过了一次次黑暗,迎接我们的却是被扣的惨不忍睹的JSF ┭┮﹏┭┮ 一.总结调研 规格的历史 传统科学的特点是发现世界,而软件的特点是构造世界.软件的最底层就是0,1,两个离散的值.程序设计语言的三 ...
- Servlet 3.0对上传的支持
Servlet 2.5 进行上传 首先对表单的要求 ->method ="post" ->enctype="multipart/form-d ...
- Task 6.2站立会议一
今天大家把这两天查的资料都拿出来整合到了一起,并仔细分析了其中的联系和区别. 因为大家每个人的思路都不一样,有各种各样的想法和不同的意见,所以最终统一意见是很难的一个过程.开始大家认我们可以做一个单独 ...
- 将url的参数解析为Json数据
代码如下: <!DOCTYPE> <html lang="en"> <head> </head> <body> < ...
- 消息队列第二篇:MessageQueue实战(课程订单)
上一篇:消息队列介绍 本篇一开始就上代码,主要演练MessageQueue的实际应用.用户提交订单(消息发送),系统将订单发送到订单队列(Order Queue)中:订单管理系统(消息接收)端,监听消 ...
- sqlserver 修改表字段长度
ALTER TABLE Table1 ALTER COLUMN column1 VARCHAR(255)
- 安恒杯2月月赛-应该不是xss
1. 打开题目一看,是个留言板 2. 查看源码发现有几个js文件 依次打开发现在main.js里存在这样一段代码 3. 访问 /#login是登录的界面,/#chgpass是修改密码的界面,其中修改密 ...
- Python模块Scrapy导入出错:ImportError: cannot import name xmlrpc_client
Mac(OS version: OS X Yosemite 10.10.5)上安装Scrapy模块,使用时出现: from six.moves import xmlrpc_client as xmlr ...