AGC012E 题解

First Post:

Last Update:

远古 AGC 还是比现代 AGC (2023) 好做多了。

首先观察跳跃操作在干什么。容易发现只会跳 次,且每次跳完后的水容量都是确定的,为

然后删掉跳跃操作,发现如果此时容量为 ,那么只能在 的绿洲之间往返,也就是说,以每个点开始,能到的段是一个区间。对每种容量和每个位置处理其能到的区间 ,这里 表示容量为 。

那么”遍历所有绿洲“相当于对每个 ,选出一个位置 ,使得最后 。询问则相当于限制 。

在没有限制时怎么做呢?设 表示用了 的区间,能覆盖的最长前缀为 ,从 转移到 时,找到 且 最大的 ,用 更新 即可。

现在考虑有限制的情形。将 代表的区间删去后,如果还可以使得并为 ,答案就全为 Possible。否则,找到一个没被覆盖的点,将其左边的区间集合设为 ,右边的就是 (这里的 不包括 )。当存在一个 使得 时,答案就是 Possible ,反之为 Impossible。

因为可能的 均只有 个,状压 DP 和最后的统计答案都可以在 的时间内解决。

我写的代码复杂度瓶颈在于找到每个 和 ,用的是二分,时间复杂度为 ,实际上可以用双指针做到 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <bits/stdc++.h>
using namespace std;
template<typename T> inline bool ckmin(T &x,const T &y) { return (x > y) && (x = y);}
template<typename T> inline bool ckmax(T &x,const T &y) { return (x < y) && (x = y);}
const int N = 2e5 + 5,Sz = 1 << 19,Lg = 19;
int n,V,lim;
int x[N];
int ST[Lg][N],lg[N]; // min
int f[Sz],g[Sz];
int L[Lg][N],R[Lg][N];
inline int Qmax(int l,int r) {
if(l > r) return 0x7f7f7f7f;
int k = lg[r - l + 1];
return max(ST[k][l],ST[k][r-(1<<k)+1]);
}
struct BIT {
int tr[N];
#define lowbit(x) (x&(-x))
inline void upd(int x,int v) { for(int i = x;i <= n + 1;i += lowbit(i)) tr[i] += v;}
inline int Sum(int x) { int res = 0;for(int i = x;i;i ^= lowbit(i)) res += tr[i];return res;}
};
BIT T;
inline void GetLR() {
lg[0] = -1;
for(int i = 1;i <= n;i++) ST[0][i] = x[i] - x[i - 1],lg[i] = lg[i >> 1] + 1;
for(int j = 1;j < lim;j++)
for(int i = 1;i + (1 << j) - 1 <= n;i++)
ST[j][i] = max(ST[j - 1][i],ST[j - 1][i + (1 << j - 1)]);
for(int t = 0;t < lim;t++) {
int nv = V >> t;
for(int i = 1;i <= n;i++) {
int lef = 2,rig = i + 1;
while(lef < rig) {
int mid = lef + rig >> 1;
if(Qmax(mid,i) <= nv) rig = mid;
else lef = mid + 1;
}
L[t][i] = lef - 1;
lef = i;rig = n;
while(lef < rig) {
int mid = lef + rig + 1 >> 1;
if(Qmax(i + 1,mid) <= nv) lef = mid;
else rig = mid - 1;
}
R[t][i] = lef;
}
}
}
int dat[Lg][N];
inline void DP() {
for(int t = 0;t < lim;t++) {
for(int i = 1;i <= n;i++) ckmax(dat[t][L[t][i]],R[t][i]);
dat[t][0] = 0;
for(int i = 1;i <= n;i++) ckmax(dat[t][i],dat[t][i - 1]);
}
f[0] = 0;
for(int S = 0;S < (1 << lim);++S)
for(int i = 0;i < lim;i++) if((~S >> i) & 1)
ckmax(f[S | (1 << i)],max(f[S],dat[i][f[S] + 1]));
for(int t = 0;t < lim;t++) {
for(int i = 0;i <= n + 1;i++) dat[t][i] = n + 1;
for(int i = 1;i <= n;i++) ckmin(dat[t][R[t][i]],L[t][i]);
for(int i = n;i >= 1;i--) ckmin(dat[t][i],dat[t][i + 1]);
}
for(int i = 0;i < (1 << lim);i++) g[i] = n + 1;
for(int S = 0;S < (1 << lim);++S)
for(int i = 0;i < lim;i++) if((~S >> i) & 1)
ckmin(g[S | (1 << i)],min(g[S],dat[i][g[S] - 1]));
}
vector<int> Cs[N],Qs[N];
int ans[N];
int main() {
cin >> n >> V;lim = __lg(V) + 2;
for(int i = 1;i <= n;i++) cin >> x[i];
GetLR();DP();
if(f[(1 << lim) - 1 - 1] >= n) {
for(int i = 1;i <= n;i++) puts("Possible");
return 0;
}
for(int S = 0;S < (1 << lim);++S)
if((~S & 1)) {
int tl = f[S],tr = g[(1 << lim) - 1 - 1 - S];
Cs[tl].push_back(tr);
}
for(int i = 1;i <= n;i++) Qs[L[0][i] - 1].push_back(i);
for(int i = n;i >= 0;i--) {
for(auto j : Cs[i]) T.upd(j,1);
for(auto id : Qs[i])
ans[id] = T.Sum(R[0][id] + 1) > 0;
}
for(int i = 1;i <= n;i++)
if(ans[i]) puts("Possible");
else puts("Impossible");
return 0;
}