题解:AT_arc072_c [ARC072E] Alice in linear land

· · 题解

题目传送门

0.前言

模拟赛里面考到了......当时想的优先队列维护,不出意外的死了...... 参考了这位 dalao 的题解

1.暴力

相信所有人看到这道题,都能想到一个显然的 DP。

状态表示:dp[i][j] 表示后 i 项能否在 d=j 的情况下走到终点。1 表示可以,反之不行。

转移方程式如下:

原因:当用后 $i+1$ 种操作能在 $j-d[i]$ 的情况下走到终点时,此时先走 $d[i]$ 再走 $j-d[i]$ 即可完成 $d=j$ 的情况。需要注意必须 $d[i] \le j$,不然就走过了。 $dp[i][j]=dp[i+1][d[i]-j]$(当 $j < d[i] \le 2 \times j$ 时) 原因:当用后 $i+1$ 种操作能在 $d[i]-j$ 的情况下走到终点时,此时先走 $d[i]$,越过终点。此时到达离终点 $d[i]-j$ 的位置。再往回走 $d[i]-j$ 即可完成即可完成 $d=j$ 的情况。需要注意必须 $j < d[i] \le 2 \times j$,不然就走不过或者大于了原距离。 $dp[i][j]=dp[i+1][j]$(当 $2 \times j < d[i]$ 时) 原因:显然如果走了 $d[i]$ 的距离会比原距离还要大,所以不走,只能让后 $i+1$ 种操作走 $j$ 的距离。 对于询问,不难发现如果询问第 $i$ 个位置对 $i$ 以前的位置走到的位置是无关的。对于对 $i$ 及以后的位置,由于 $i$ 可以随便改,所以只要离终点的距离是在前 $i$ 个位置离终点的位置以内的距离,都是可以的。(如果大于不会移动),此时我们设前 $i$ 个位置离终点的距离为 $x$,那么只有 $i+1$ 及以后的位置可以处理 $j \in 0 \sim x$ 的所有情况,才输出 `NO`。 显然,这是一个 $O(nD)$ 的做法,尝试优化。 ## 2.优化 观察到,我们仅需找到 $dp[i+1][j\in 0 \sim x]$ 中是否存在 $0$ 即可。观察到 $dp[i+1]$ 中 $j$ 是越小越容易影响答案的(因为查询范围最小都是 $0$),所以我们希望仅维护 $dp[i]$ 表示 $dp[i][j]$ 中最小的 $j$。我们来尝试表示一下状态转移方程式。 $dp[i]=dp[i+1]$(当 $d[i] \ge 2 \times dp[j+1]$ 时,对应第三个方程式) $dp[i]=d[i]-dp[i+1]$(当 $d[i] > 2 \times dp[j+1]$ 时,对应第二个方程式) $dp[i]=dp[i+1]+d[i]$(当 $d[i] \le dp[i+1]+d[i]$ 时,由于这显然成立,所以等于无条件,对应第一个方程式) 核心逻辑:$dp[i]=dp[i][minj]=$ 转移所用 $j$。 根据上述三个式子转移即可。 对于询问,只需判断 $dp[i]$ 与 $x$ 的大小关系即可。 ## 3.代码 ```cpp #include <bits/stdc++.h> using namespace std; int n,d,a[500005],pl,q,uo,pre[500005],dp[500005],now; bool can[500005]; int main(){ scanf("%d%d",&n,&d); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(abs(now-d)>abs(now+a[i]-d)){ now+=a[i]; } if(abs(now-d)>abs(now-a[i]-d)){ now-=a[i]; } pre[i]=abs(now-d);//计算x } pre[0]=d; dp[n+1]=1; for(int i=n;i>=1;i--){ if(a[i]>=2*dp[i+1]){ dp[i]=dp[i+1];//方程1 if(a[i]>2*dp[i+1]){ dp[i]=min(dp[i],a[i]-dp[i+1]);//方程2 } } else{ dp[i]=dp[i+1]+a[i];//方程3,因为明显方程1优于方程3,所以放在else里 } } scanf("%d",&q); can[n+1]=0; for(int i=1;i<=q;i++){ scanf("%d",&uo); if(dp[uo+1]<=pre[uo-1]){//判断 printf("YES\n"); } else{ printf("NO\n"); } } return 0; } //6 ```