题目大意:
  有一排n个格子和2枚硬币。
  现在有q次任务,每一次要你把其中一枚硬币移到x的位置上,移动1格的代价是1。
  两枚硬币不能同时移动,任务必须按次序完成。
  现在告诉你两枚硬币初始状态所在的位置a和b,问完成所有任务的最小代价。

思路:
  很容易想到一个O(qn)的DP。
  由于完成任务的次序确定,每个任务的位置也确定,我们可以用f[i][j]表示完成第i个任务后,一个硬币在x[i],一个硬币在j的最小代价。
  转移方程为f[i][j]=min{f[i-1][j]+|x[i]-x[i-1]|},f[i][a[i-1]]=min{f[i-1][j]+|x[i]-j|}。
  然而这样还是会TLE,在AtCoder上只过了14/34的测试数据。
  不难发现,在状态转移方程中,如果我们能去掉绝对值,里面的东西就能用线段树维护。
  而绝对值的取值只与硬币的左右位置关系有关。
  因此我们可以建2棵线段树,一棵表示被转移的状态在目标状态左边,一棵表示在右边。
  左线段树中每个叶子结点x[i-1]维护f[i-1][j]-x[i-1]的值,右线段树每个叶子结点x[i-1]维护f[i-1][j]+x[i-1]的值。
  看了一下榜,发现排在前面的基本上都是用树状数组做的。
  然而用树状数组维护区间最值难道不是O(log^2 n)的吗?
  事实上我们可以发现线段树上维护的东西只会越来越小,这样我们可以直接在树状数组上修改,不用考虑原来的最小值没了怎么办。
  然后我又在树状数组里面加了一个剪枝。
  这样随随便便就能拿Rank1。

 #include<cstdio>
#include<cctype>
#include<cstdlib>
#include<algorithm>
typedef signed long long int int64;
inline unsigned getint() {
register char ch;
while(!isdigit(ch=getchar()));
register unsigned x=ch^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return x;
}
inline int64 min(const int64 &a,const int64 &b) {
return a<b?a:b;
}
const int64 inf=0x7ffffffffffffff;
const int N=;
int n;
class FenwickTree {
private:
int64 val[N];
int lowbit(const int &x) const {
return x&-x;
}
public:
FenwickTree() {
std::fill(&val[],&val[N],inf);
}
void modify(int p,const int64 &x) {
while(p<=n) {
if(x<val[p]) {
val[p]=x;
} else {
return;
}
p+=lowbit(p);
}
}
int64 query(int p) const {
int64 ret=inf;
while(p) {
ret=min(ret,val[p]);
p-=lowbit(p);
}
return ret;
}
};
FenwickTree ta;
class RevFenwickTree {
private:
int64 val[N];
int lowbit(const int &x) const {
return x&-x;
}
public:
RevFenwickTree() {
std::fill(&val[],&val[N],inf);
}
void modify(int p,const int64 &x) {
while(p) {
if(x<val[p]) {
val[p]=x;
} else {
return;
}
p-=lowbit(p);
}
}
int64 query(int p) const {
int64 ret=inf;
while(p<=n) {
ret=min(ret,val[p]);
p+=lowbit(p);
}
return ret;
}
};
RevFenwickTree tb;
int64 f[N];
inline void modify(const int &p,const int64 x) {
if(x<f[p]) {
f[p]=x;
ta.modify(p,x-p);
tb.modify(p,x+p);
}
}
int main() {
n=getint();
int q=getint(),a=getint(),b=getint();
std::fill(&f[],&f[N],inf);
modify(a,);
int64 sum=;
while(q--) {
a=b;
b=getint();
sum+=abs(a-b);
int64 t1=ta.query(b)+b,t2=tb.query(b)-b;
modify(a,min(t1,t2)-abs(a-b));
}
int64 tmp=inf;
for(register int i=;i<=n;i++) {
tmp=min(tmp,f[i]);
}
printf("%lld\n",tmp+sum);
return ;
}

原来的O(n^2)DP程序:

 #include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
inline unsigned getint() {
register char ch;
while(!isdigit(ch=getchar()));
register unsigned x=ch^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return x;
}
inline unsigned min(const unsigned &a,const unsigned &b) {
return a<b?a:b;
}
const unsigned N=;
unsigned long long f[][N];
unsigned a[];
int main() {
unsigned n=getint(),q=getint();
memset(f[],0xff,n<<);
a[]=getint()-,f[][getint()-]=;
for(register unsigned i=;i<=q;i++) {
a[i&]=getint()-;
memset(f[i&],0xff,n<<);
for(register unsigned j=;j<n;j++) {
if(!~f[~i&][j]) continue;
f[i&][j]=min(f[i&][j],f[~i&][j]+abs(a[i&]-a[~i&]));
f[i&][a[~i&]]=min(f[i&][a[~i&]],f[~i&][j]+abs(a[i&]-j));
}
}
unsigned long long ans=~;
for(register unsigned i=;i<n;i++) {
ans=min(ans,f[q&][i]);
}
printf("%llu\n",ans);
return ;
}

[AtCoder-ARC073F]Many Moves的更多相关文章

  1. AtCoder刷题记录

    构造题都是神仙题 /kk ARC066C Addition and Subtraction Hard 首先要发现两个性质: 加号右边不会有括号:显然,有括号也可以被删去,答案不变. \(op_i\)和 ...

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

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

  3. 【ARC073F】Many Moves

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

  4. [atcoder contest 010] F - Tree Game

    [atcoder contest 010] F - Tree Game Time limit : 2sec / Memory limit : 256MB Score : 1600 points Pro ...

  5. AtCoder Beginner Contest 151 题解报告

    总的来说,这次的题目比较水,然而菜菜的我并没有把所有题目都做完,话不多说,直接来干货: A:Next Alphabet 题目链接:https://atcoder.jp/contests/abc151/ ...

  6. [LeetCode] Minimum Moves to Equal Array Elements II 最少移动次数使数组元素相等之二

    Given a non-empty integer array, find the minimum number of moves required to make all array element ...

  7. [LeetCode] Minimum Moves to Equal Array Elements 最少移动次数使数组元素相等

    Given a non-empty integer array of size n, find the minimum number of moves required to make all arr ...

  8. LeetCode Minimum Moves to Equal Array Elements II

    原题链接在这里:https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/ 题目: Given a non-empt ...

  9. LeetCode Minimum Moves to Equal Array Elements

    原题链接在这里:https://leetcode.com/problems/minimum-moves-to-equal-array-elements/ 题目: Given a non-empty i ...

  10. LeetCode 453 Minimum Moves to Equal Array Elements

    Problem: Given a non-empty integer array of size n, find the minimum number of moves required to mak ...

随机推荐

  1. ASP.NET EF(LINQ/Lambda查询)

    EF(EntityFrameWork) ORM(对象关系映射框架/数据持久化框架),根据实体对象操作数据表中数据的一种面向对象的操作框架,底层也是调用ADO.NET ASP.NET MVC 项目会自动 ...

  2. css_input[checked]复选框去掉默认样式并添加新样式

    效果对比: “\2713”实体符号√ :如有兴趣查看详细实体符号请点这里 代码实现: <!DOCTYPE html> <html> <head> <meta ...

  3. 黑色的网站后台管理系统ui界面——后台

    链接:http://pan.baidu.com/s/1pLffwE3 密码:m4v6

  4. Coursera在线学习---第二节.Octave学习

    1)两个矩阵相乘 A*B 2)两个矩阵元素位相乘(A.B矩阵中对应位置的元素相乘) A.*B 3)矩阵A的元素进行平方 A.^2 4)向量或矩阵中的元素求倒数 1./V    或   1./A 5) ...

  5. 5-python的封装与结构 - set集合

    目录 1 封装与解构 1.1 封装 1.2 解构 1.3 Python3的解构 2 set类型 2.1 set的定义 2.2 set的基本操作 2.2.1 增加元素 2.2.2 删除元素 2.2.3 ...

  6. elasticsearch集群介绍及优化【转】

    elasticsearch用于构建高可用和可扩展的系统.扩展的方式可以是购买更好的服务器(纵向扩展)或者购买更多的服务器(横向扩展),Elasticsearch能从更强大的硬件中获得更好的性能,但是纵 ...

  7. The algorithm of entropy realization

    近似熵的一种快速实用算法 Pincus提出的近似熵算法中有很多冗余的计算,效率低,速度慢,不利于实际应用,洪波等人在定义的基础上引入二值距离矩阵的概率,提出了一种实用快速的算法. function A ...

  8. C 中级 - SO_REUSEPORT 和 SO_REUSEADDR

    引言 - 问题由来 刚开始学习网络编程时候, 常听到一个词, 先开启 "端口复用 SO_REUSEADDR". 那时很一知半解, 就知道该那么写了. 心里一直有些奇怪, 语义不通呀 ...

  9. ubuntu查看mysql版本的几种方法

    ubuntu查看mysql版本的几种方法 mysql 1:在终端下:mysql -V(大写) //代码 $ mysql -V mysql Ver 14.14 Distrib 5.5.46, for d ...

  10. 让Linux应用更加得心应手的

    1.计算文件数和目录数  下面的语句可以帮你计算有多少个文件和多少个目录 # ls -l * |grep "^-"|wc -l ---- to count files # ls - ...