[DP记录]AT2558 [ARC073D] Many Moves

command_block

2021-05-02 14:09:47

Personal

**题意** : 在一行中有 $n$ 个格子,编号为 $1\sim n$ 。 有 $2$ 颗棋子,初始时分别位于位置 $a$ 和 $b$ 。 按顺序给出 $q$ 个要求: - 给出位置 $x_i$ ,要求将两个棋子中任意一个移动到位置 $x_i$ 。 就是说将棋子从 $x$ 位置移动到 $y$ 位置需要花费 $|x-y|$ 秒。允许两棋子在同一个位置上。 问满足所有要求所需的最小时间。 $n,q\leq 2\times 10^5$ ,时限$\texttt{2s}$。 ------------ 考虑 $\rm DP$ ,记 $dp[i][x][y]$ 为 : 在处理完前 $i$ 个要求后,两个棋子的位置分别为 $x,y$ ,所需的最小时间。 可以发现,当处理完第 $i$ 个要求时,必定有一个棋子在 $x_i$ 处,故状态可以简化为 : $dp[i][y]$ 表示在处理完前 $i$ 个要求后,一个棋子在 $x_i$ 处,另一个在 $y$ 处,所需的最小时间。 转移如下 : $$ \begin{aligned} {\color{white}\Big|}dp[i+1][y]&=dp[i][y]+|x_i-x_{i+1}|\\ dp[i+1][x_{i}]&=\min dp[i][y]+|y-x_{i+1}|\\ &=\min\Big\{\min_{y\in[1,x_{i+1}]}x_{i+1}+dp[i][y]-y,\ \min_{y\in(x_{i+1},n]}-x_{i+1}+dp[i][y]+y\Big\}\\ \end{aligned} $$ 只需维护 $dp[i][y]-y,dp[i][y]+y$ ($±y$ 也容易写进一次函数)的区间 $\min$ 即可处理第二种转移。 第一种转移不便维护,记 $s_i=\sum\limits_{k=1}^{i-1}|x_i-x_{i+1}|$ ,改为记录 $dp'[i][y]=dp[i][y]-s_i$。 这样,第一种转移对 $dp'[i][y]$ 是没有影响的。 第二种转移中额外减去 $|x_{i}-x_{i+1}|$ 即可。 使用(两颗)线段树维护,复杂度 $O(n\log n)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 200500 using namespace std; const ll INF=1ll<<60; int n,to,wfl,wfr;ll wfc,ret; struct SGT { ll a[MaxN<<2]; void build(int l,int r,int u) { a[u]=INF; if (l==r)return ; int mid=(l+r)>>1; build(l,mid,u<<1); build(mid+1,r,u<<1|1); } inline void up(int u) {a[u]=min(a[u<<1],a[u<<1|1]);} void chg(int l,int r,int u) { if (l==r){a[u]=wfc;return ;} int mid=(l+r)>>1; if (to<=mid)chg(l,mid,u<<1); else chg(mid+1,r,u<<1|1); up(u); } void chg(int p,ll c) {to=p;wfc=c;chg(1,n,1);} void qry(int l,int r,int u) { if (wfl<=l&&r<=wfr) {ret=min(ret,a[u]);return ;} int mid=(l+r)>>1; if (wfl<=mid)qry(l,mid,u<<1); if (mid<wfr)qry(mid+1,r,u<<1|1); } ll qry(int l,int r) {wfl=l;wfr=r;ret=INF;qry(1,n,1);return ret;} void dfs(int l,int r,int u) { if (l==r){ret=min(ret,a[u]-l);return ;} int mid=(l+r)>>1; dfs(l,mid,u<<1); dfs(mid+1,r,u<<1|1); } }T1,T2; int m,x[MaxN],y; int main() { scanf("%d%d%d%d",&n,&m,&x[0],&y); T1.build(1,n,1);T2.build(1,n,1); T1.chg(y,y);T2.chg(y,-y); for (int i=1;i<=m;i++){ scanf("%d",&x[i]); ll sav=min(x[i]+T2.qry(1,x[i]),-x[i]+T1.qry(x[i],n)); sav=sav-(x[i]>x[i-1] ? x[i]-x[i-1] : x[i-1]-x[i]); T1.chg(x[i-1],sav+x[i-1]);T2.chg(x[i-1],sav-x[i-1]); } ret=INF;T1.dfs(1,n,1); for (int i=1;i<=m;i++) ret+=(x[i]>x[i-1] ? x[i]-x[i-1] : x[i-1]-x[i]); printf("%lld",ret); return 0; } ```