Portal -->arc073D

Description

​  有\(n\)个格子,编号从左到右为\(1\sim n\),一开始有两个棋子,位置给定,接下来要依次进行\(Q\)次操作,第\(i\)次操作必须选择一个棋子将其移动到\(x_i\)上面(\(x\)数组给定),所需代价是当前位置与目标位置的编号之差绝对值,允许两个棋子在同一个位置,求完成操作的最小代价

​  

Solution

​  大家好我是一个不会算复杂度的弱智选手

​  这题的话。。没有什么想法那就快乐dp咯。。但是状态的设置比较重要,注意到两个棋子其实本质上没有任何区别,我们希望用一种简明的方式将两个棋子的位置都记录下来,然后再注意到第\(i\)次操作完后一定有一个棋子在\(x_i\),那么我们可以用\(f[i][j]\)表示第\(i\)次操作完之后,不在\(x_i\)位置上的那个棋子在\(j\)这个位置,所用的最小代价

​​  那么可以写出转移:

\[f[i][j]=\begin{cases}
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的更多相关文章

  1. 【arc073f】Many Moves(动态规划,线段树)

    [arc073f]Many Moves(动态规划,线段树) 题面 atcoder 洛谷 题解 设\(f[i][j]\)表示第一个棋子在\(i\),第二个棋子在\(j\)的最小移动代价. 发现在一次移动 ...

  2. 【bfs】Knight Moves

    [题目描述] 输入nn代表有个n×nn×n的棋盘,输入开始位置的坐标和结束位置的坐标,问一个骑士朝棋盘的八个方向走马字步,从开始坐标到结束坐标可以经过多少步. [输入] 首先输入一个nn,表示测试样例 ...

  3. 【ARC073F】Many Moves

    题目 一个显然的\(dp\),设\(dp_{i,j}\)表示其中一个棋子在\(x_i\)点,另一个棋子在\(j\)点的最小花费 显然\(dp_{i,j}\)有两种转移 第一种是把\(x_i\)上的棋子 ...

  4. 【LeetCode】数学(共106题)

    [2]Add Two Numbers (2018年12月23日,review) 链表的高精度加法. 题解:链表专题:https://www.cnblogs.com/zhangwanying/p/979 ...

  5. 【习题 6-4 UVA-439】Knight Moves

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] bfs模板题 [代码] /* 1.Shoud it use long long ? 2.Have you ever test sev ...

  6. 【广搜】Knight Moves

    题目描述 Mr Somurolov, fabulous chess-gamer indeed, asserts that no one else but him can move knights fr ...

  7. 1450:【例 3】Knight Moves

    1450:[例 3]Knight Moves  题解 这道题可以用双向宽度搜索优化(总介绍在  BFS ) 给定了起始状态和结束状态,求最少步数,显然是用BFS,为了节省时间,选择双向BFS. 双向B ...

  8. 【模拟】Codeforces 710A King Moves

    题目链接: http://codeforces.com/problemset/problem/710/A 题目大意: 国际象棋标准8X8棋盘,国王能往周围8个方向走.输入国王的位置,输出当前国王能往几 ...

  9. 带给你灵感:30个超棒的 SVG 动画展示【上篇】

    前端开发人员和设计师一般使用 CSS 来创建 HTML 元素动画.然而,由于 HTML 在创建图案,形状,和其他方面的局限性,它们自然的转向了 SVG,它提供了更多更有趣的能力.借助SVG,我们有更多 ...

随机推荐

  1. CUDA、CUDNN在Mac Book Pro上安装的问题

    由于原版MacOS自带Nvidia驱动版本过低,导致最新版本CUDA安装后无法运行.具体症状为:在编译时一切正常,在运行CUDA相关程序时报错: CUDA driver version is insu ...

  2. sql语句(Mysql数据库)

    Mysql数据库的sql语句: 一.基本操作 1.连接数据库 mysql -uroot -proot -hlocalhost -P3306 (-u表示用户名,-p密码,-h主机,-P端口号) 2.选择 ...

  3. TIME_WAIT 你好!

    [root@vm-10-124-66-212 ~]# netstat -an|awk -F ' ' '{print $NF}'|sort |uniq -c |sort -rn|more 5552 TI ...

  4. 移动设备检测类Mobile_Detect.php

    移动设备检测类Mobile_Detect.php http://mobiledetect.net/ 分类:PHP 时间:2015年11月28日 Mobile_Detect.php是一个轻量级的开源移动 ...

  5. Alpha阶段产品功能说明

    先展示一下我们的功能流程图吧~ 一.学生用户 1. 学生登陆注册 BuaaClubs是北航所有在校生都可以注册登录的网站. 登陆界面是这样哒~ 2. 浏览报名活动 同学们可以在这个网站上查看所有社团发 ...

  6. C++:内存分区

    前言:最近正在学习有关static的知识,发觉对C++的内存分区不是很了解,上网查了很多资料,遂将这几天的学习笔记进行了简单整理,发表在这里 • 栈区(stack):主要用来存放函数的参数以及局部变量 ...

  7. datetime类型转换

    Select CONVERT(varchar(100), GETDATE(), 8):14:53:14 Select CONVERT(varchar(100), GETDATE(), 9): 06  ...

  8. mac上查找nginx安装位置

    在终端输入: nginx -V 查看nginx版本及安装的本地位置 ngxin -v 查看nginx版本(此方法依然可以检测是否安装某一软件,如git,hg等)

  9. redux相关专业名词及函数提要

    redux: 用来管理react app 状态(state)的一个架构. store: 通过createStore()创建,用来存放state,与react app是完全分离的.createStore ...

  10. cxGrid 单元格回车移到下一行,当移到最后一个单元格时回车新增一行【转】

    1 在TcxGridDBTableView中,设定属性 NewItemRow.Visible = True 2 在cxgrid中输入数据怎样回车换行  在TcxGridDBTableView中  将属 ...