【题解】[CEOI2014] Cake
先不考虑修改操作,我们发现可以将序列从 \(a\) 的位置分开,两边独立。我们只考虑左边的情况,右边同理。
如果两个相邻的位置满足 \(d_i < d_{i + 1}\),那么 \(i + 1\) 被吃了,\(i\) 将紧接着被吃掉,所以可以将它们缩成一个点,相当于每个位置都会和它前面的最大值合并,最后得到若干连续段。那么查询一个位置就相当于查询比所在段最大值小的段的总和。
现在我们考虑修改操作,也就是要动态维护每个段,可以记录 \(f_i\) 表示 \(i\) 前面(包括自己)的最大值所在的位置,一次修改肯定是对 \(f\) 从 \(x\) 开始的一个连续段,所以我们用线段树维护 \(f\),每次直接二分端点即可,复杂度为 \(\mathcal{O}(\log^2 N)\)。查询操作同样是二分加线段树。
同时我们还要处理排名问题,观察到 \(e\) 非常小,所以每次修改,排名 \(>10\) 的位置相对大小不变,我们只用记录最大的 \(10\) 个数,其余的数记录一个标号即可。
时间复杂度 \(\mathcal{O}(Q\log^2 N)\),可以线段树二分做到 \(\mathcal{O}(Q\log N + N)\)。代码有点小长但不难写。
#define N 250005
int n, s, f[N], d[N];
struct node{int l, r, tag, val;}a[N << 2];
#define L a[x].l
#define R a[x].r
#define ls (x << 1)
#define rs (ls | 1)
#define S a[x].val
#define T a[x].tag
void build(int x,int l,int r){
L = l, R = r;
if(l == r)S = f[l];
else{
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
}
}
inline void pushup(int x,int w){S = T = w;}
inline void down(int x){if(T)pushup(ls, T), pushup(rs, T), T = 0;}
void modify(int x,int l,int r,int w){
if(L >= l && R <= r)pushup(x, w);
else{
down(x); int mid = (L + R) >> 1;
if(mid >= l)modify(ls, l, r, w);
if(mid < r)modify(rs, l, r, w);
}
}
int ask(int x,int ps){
if(L == R)return S;
down(x); int mid = (L + R) >> 1;
if(mid >= ps)return ask(ls, ps);
return ask(rs, ps);
}
int p[N], u[15], id = 1e6;
void init(){
rp(i, n){
int k = n + 1 - d[i];
if(k <= 10)u[k] = i, p[i] = -k;
else p[i] = id + k - 10;
}
}
void move(int x,int y){
if(p[x] < 0){
int k = -p[x];
pre(i, k, y + 1)p[u[i - 1]]--, u[i] = u[i - 1];
p[x] = -y, u[y] = x;
}
else{
p[u[10]] = --id;
pre(i, 10, y + 1)p[u[i - 1]]--, u[i] = u[i - 1];
p[x] = -y, u[y] = x;
}
}
bool cal(int x,int y){ // x > y
if(p[x] < 0 && p[y] > 0)return true;
if(p[x] > 0 && p[y] < 0)return false;
if(p[x] < 0)return p[x] > p[y];
return p[x] < p[y];
}
int main() {
read(n, s);
rp(i, n)read(d[i]);
int mx = 0, ps = 0;
rep(i, s + 1, n){
if(d[i] > mx)mx = d[i], ps = i;
f[i] = ps;
}
mx = ps = 0;
pre(i, s - 1, 1){
if(d[i] > mx)mx = d[i], ps = i;
f[i] = ps;
}
build(1, 1, n), init();
int q; read(q);
while(q--){
char op[2]; int x, y;
scanf("%s", op);
if('E' == *op){
read(x, y), move(x, y);
if(x < s){
int o = ask(1, x);
if(cal(o, x))continue;
int lm = x;
if(o == x){
int l = 1, r = x - 1, ed = x - 1;
while(l <= r){
int mid = (l + r) >> 1;
if(ask(1, mid) == x)r = ed = mid - 1;
else l = mid + 1;
}
lm = ed;
}
if(lm){
int l = 1, r = lm, ed = lm + 1;
while(l <= r){
int mid = (l + r) >> 1;
if(cal(x, ask(1, mid)))ed = mid, r = mid - 1;
else l = mid + 1;
}
if(ed <= lm)modify(1, ed, lm, x);
}
}
else if(x > s){
int o = ask(1, x);
if(cal(o, x))continue;
int lm = x;
if(o == x){
int l = x + 1, r = n, ed = x + 1;
while(l <= r){
int mid = (l + r) >> 1;
if(ask(1, mid) == x)l = ed = mid + 1;
else r = mid - 1;
}
lm = ed;
}
if(lm <= n){
int l = lm, r = n, ed = lm - 1;
while(l <= r){
int mid = (l + r) >> 1;
if(cal(x, ask(1, mid)))ed = mid, l = mid + 1;
else r = mid - 1;
}
if(lm <= ed)modify(1, lm, ed, x);
}
}
}
else{
read(x);
if(x < s){
int l = s + 1, r = n, ed = s, w = ask(1, x);
while(l <= r){
int mid = (l + r) >> 1;
if(cal(w, ask(1, mid)))ed = mid, l = mid + 1;
else r = mid - 1;
}
printf("%d\n", ed - x);
}
else if(x > s){
int l = 1, r = s - 1, ed = s, w = ask(1, x);
while(l <= r){
int mid = (l + r) >> 1;
if(cal(w, ask(1, mid)))ed = mid,r = mid - 1;
else l = mid + 1;
}
printf("%d\n", x - ed);
}
else puts("0");
}
}
return 0;
}