zoukankan      html  css  js  c++  java
  • [AT2558]Many Moves

    题目大意:有$n$个位置$1,2,dots n$;你有两个棋子$A$和$B$,你要进行$q$次操作,第$i$次操作给定一个$x_i$,你要选择一个棋子移动到$x_i$;求两个棋子最小移动的步数之和。

    题解:一个$O(n^2)$的$DP$容易想到$f_{i,j}$表示到了第$i$步,另一个棋子在$j$这个位置。

    $$f_{i,x_{i-1}}=min{f_{i-1,j}+|x_i-j|}$$

    $$f_{i,j}=f_{i-1,j}+|x_i-x_{i-1}|$$

    下面一个还好做,可上面一个呢?

    可以考虑拆成$jleq x_i$和$j>x_i$来做

    $$ herefore f_{i,x_{i-1}} =
    egin{cases}
    f_{i-1,j}+x_i-jquad(jleq x_i)\
    f_{i-1,j}+j-x_iquad(j>x_i)
    end{cases}$$

    然后发现是区间修改求最小值,可以用线段树来做。

    卡点:1.转移时把$x_{i-1}$写成了$x_{i}$

    C++ Code:

    #include <cstdio>
    #include <cstring>
    #define maxn 200010
    using namespace std;
    const long long inf = 0x3f3f3f3f3f3f3f3f;
    inline long long min(long long a, long long b) {return a < b ? a : b;}
    inline long long abs(long long a) {return a > 0 ? a : -a;}
    int n, k, x, y;
    int q, last;
    long long V[maxn << 2][3], cov[maxn << 2];
    void pushdown(int rt) {
    	int lc = rt << 1, rc = rt << 1 | 1;
    	long long &tmp = cov[rt];
    	V[lc][0] += tmp;
    	V[lc][1] += tmp;
    	V[lc][2] += tmp;
    	cov[lc] += tmp;
    	V[rc][0] += tmp;
    	V[rc][1] += tmp;
    	V[rc][2] += tmp;
    	cov[rc] += tmp;
    	tmp = 0;
    }
    void update(int rt) {
    	int lc = rt << 1, rc = rt << 1 | 1;
    	V[rt][0] = min(V[lc][0], V[rc][0]);
    	V[rt][1] = min(V[lc][1], V[rc][1]);
    	V[rt][2] = min(V[lc][2], V[rc][2]);
    }
    void add(int rt, int l, int r, int p, long long num) {
    	if (l == r) {
    		V[rt][0] = num;
    		V[rt][1] = num + l;
    		V[rt][2] = num - l;
    		return ;
    	}
    	if (cov[rt]) pushdown(rt);
    	int mid = l + r >> 1;
    	if (p <= mid) add(rt << 1, l, mid, p, num);
    	else add(rt << 1 | 1, mid + 1, r, p, num);
    	update(rt);
    }
    void add(long long num, int rt = 1) {
    	V[rt][0] += num;
    	V[rt][1] += num;
    	V[rt][2] += num;
    	cov[rt] += num;
    }
    long long ask(int rt, int l, int r, int L, int R, int op) {
    	if (L <= l && R >= r) return V[rt][op];
    	pushdown(rt);
    	int mid = l + r >> 1;
    	long long ans = inf;
    	if (L <= mid) ans = ask(rt << 1, l, mid, L, R, op);
    	if (R > mid) ans = min(ans, ask(rt << 1 | 1, mid + 1, r, L, R, op));
    	return ans;
    }
    int main() {
    	scanf("%d%d%d%d", &n, &k, &x, &y);
    	scanf("%d", &q);
    	memset(V, 0x3f, sizeof V);
    	add(1, 1, n, x, abs(y - q));
    	add(1, 1, n, y, abs(x - q));
    	while (--k) {
    		last = q; scanf("%d", &q);
    		long long t0 = ask(1, 1, n, last, last, 0) + abs(q - last);
    		long long t1 = ask(1, 1, n, q, n, 1) - q;
    		long long t2 = ask(1, 1, n, 1, q, 2) + q;
    		long long ans = min(t0, min(t1, t2));
    		add(abs(q - last));
    		add(1, 1, n, last, ans);
    	}
    	printf("%lld
    ", V[1][0]);
    	return 0;
    }
    

      

  • 相关阅读:
    Java 反射机制 ( Java Reflection Mechanism )
    Excel&合并单元格内容无效
    UNIX环境高级编程(19-伪终端)
    UNIX环境高级编程(18-终端I/O)
    UNIX环境高级编程(15-进程间通信)
    UNIX环境高级编程(14-高级I/O)
    UNIX环境高级编程(13-守护进程)
    UNIX环境高级编程(12-线程控制)
    UNIX环境高级编程(11-线程)
    C专家编程(4)
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/9466135.html
Copyright © 2011-2022 走看看