原题链接
https://arc072.contest.atcoder.jp/tasks/arc072_c
Description
Alice在一个数轴上,一开始她到原点有D的距离(位置正负不论)
给出一个长度为N的正整数序列a
她会按照a从1到N顺次操作,第i次操作的移动距离为a[i],若朝着原点方向移动a[i]的长度(可能跨过原点)会比目前到原点的距离严格更近,那么她就会走,否则不走
现在有Q个询问,每次询问是否能够通过修改第x个数,使得Alice不能到达原点
N,Q<=500000
D,a<=1e9
Solution
思考一下我们是如何暴力的
显然正负没有用,我们只考虑距离
先预处理出做了前i次操作离原点的距离c[i]
设0/1状态
gi,j
g
i
,
j
表示站在距离原点为j的位置,后i~n次操作能否到达原点
然而事实上,我们并不需要维护每一个距离的0/1状态
只需要找到一个离原点最近的不能到原点的距离,只要这个距离小于等于当前的距离,那么我们就可以通过修改这一个a使得它走到这个点去,以后就都不能走到原点了
所以我们设
F[i]
F
[
i
]
表示距离原点最短的经过后i次操作不能到达原点的距离
即
gi,0...F[i]−1=1,gi,F[i]=0
g
i
,
0...
F
[
i
]
−
1
=
1
,
g
i
,
F
[
i
]
=
0
现在只需要考虑F的转移
从后往前做,每一次前面新加入的一个a[i],因为第i次以后的操作都不能使F[i+1]到达原点了,并且F[i+1]已经是最短的不能到达的距离,那么只要这一个a能够使当前距离更近原点,那么这一个距离就可以到达了,需要找到下一个不能到达原点的距离
可以发现下一个转移到的距离必是
F[i+1]+a[i]
F
[
i
+
1
]
+
a
[
i
]
,这个位置经过第i次操作刚好走到F[i+1]的位置,然而后i+1次操作又不能走到原点
并且F[i+1]到F[i+1]+a[i]中间的点经过a[i]都会走的比F[i+1]更近,那显然是可以走到原点的
这样就可以通过O(N)的DP做完了
Code
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 500005
using namespace std;
int f[N],c[N],n,m,a[N];
int main()
{
cin>>n>>m;
fo(i,1,n) scanf("%d",&a[i]);
int d=m;
fo(i,1,n)
{
if(abs(d-a[i])<d) d=abs(d-a[i]);
c[i]=d;
}
f[n+1]=1;
fod(i,n,1)
{
f[i]=f[i+1];
if(abs(f[i]-a[i])<f[i]) f[i]=f[i]+a[i];
}
int q;
cin>>q;
c[0]=m;
fo(i,1,q)
{
int x;
scanf("%d",&x);
if(c[x-1]>=f[x+1]) printf("YES\n");
else printf("NO\n");
}
}