Loading

【题解】[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;
}
posted @ 2022-07-22 15:13  7KByte  阅读(49)  评论(0编辑  收藏  举报