[BZOJ3642][CEOI 2014] Cake 线段树

题意:

Leopold买了n块蛋糕, 这些蛋糕排成一排,从左到右记为1到n,第i块蛋糕最初的美味度为di。
Leopold每次固定最先吃第k块蛋糕,于是位置k就空出来了。之后他吃的每一块蛋糕总是位于某个空的位置旁边,并且是美味度最低的一块。因此在任何时刻,所有空的位置是一段连续的区间。Leopold会装饰自己的蛋糕来增加它的美味度,被加过装饰的蛋糕一定会成为最美味的10个蛋糕之一,任何时候都不存在两块美味度相同的蛋糕。
Leopold想知道在他吃到某块蛋糕b之前需要吃掉多少块蛋糕。

输入:

第一行两个整数n和k,表示蛋糕的数量和Leopold吃的第一块蛋糕的位置。
第二行n个不同的整数d1,d2...,dn,表示第i块蛋糕最初的美味度。
第三行一个整数q,表示操作的数量。
下面q行会包括以下两种操作。
(1)E i e 蛋糕i被装饰成了第e美味的蛋糕(即原来前e-1美味的蛋糕不变,原来第e美味的蛋糕变成了第e+1美味的蛋糕,以此类推)注意每次装饰一定会增加美味度。
(2)F b输出Leopold吃到蛋糕b之前需要吃掉多少块蛋糕。

输出:

对于每个F操作,输出一行一个整数,表示蛋糕的数量。

样例略

思路:

先考了不带修改的情况:

先按照最初的美味值进行编号,放到一棵线段树(当然根据K分成两边放到两个线段树上也可以)。

假设我们需要查询的\(x\)\(k\)的左端,我们可以先查询\([x,k-1]\)这段区间内排名的最小值\(Mi\),分析能吃掉这个排名最小(即美味值最高的蛋糕)的情况是在\(k\)的右边存在的一个蛋糕排名更小或者是右边不存在蛋糕了,因此我们要找到右边区间内,第一个小于当前\(Mi\)的位置。

(不带修改的当然可以一次直接用数组处理所有的蛋糕第几个吃掉,但这对后面的写法没有任何帮助)

接下来考虑修改:

当我们将\(x\)的排名变成\(e\)时,我们可以直接将\(x\)的排名变成当前第一的排名-1(因为这里是按照排名越小美味值越高算的),对于\([1,e-1]\)区间内的蛋糕,将它们的排名再依次进行减1

因为\(e\)的范围是在\([1,10]\)之内的,所以我们只用记录前十的蛋糕是哪一些。

更新排名的时候有一个注意点:

如果\(x\)的原来排名是\(7\),变成了\(3\),那么需要进行移动的排名是\([3,6]\)而不是\([3,10]\),所以要特别判一下\(x\)的排名是不是已经在前十当中;

#include<bits/stdc++.h>
#define M 250005
using namespace std;
int n,K,q,Rank[M],Id[M];
struct node {
	int id,x;
	bool operator<(const node&_)const {
		return x>_.x;
	}
} a[M];
struct Tree {
	struct Node {
		int mi;
	} tree[M<<2];
	void up(int p) {
		tree[p].mi=min(tree[p<<1].mi,tree[p<<1|1].mi);
	}
	void updata(int L,int R,int x,int v,int p) {
		if(L==R) {
			tree[p].mi=v;
			return;
		}
		int mid=(L+R)>>1;
		if(x<=mid)updata(L,mid,x,v,p<<1);
		else updata(mid+1,R,x,v,p<<1|1);
		up(p);
	}
	void build(int l,int r,int p) {
		if(l==r) {
			tree[p].mi=Id[l];
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,p<<1),build(mid+1,r,p<<1|1);
		up(p);
	}
	int Query(int L,int R,int l,int r,int p) {
		if(L==l&&R==r)return tree[p].mi;
		int mid=(L+R)>>1;
		if(r<=mid)return Query(L,mid,l,r,p<<1);
		else if(l>mid)return Query(mid+1,R,l,r,p<<1|1);
		else return min(Query(L,mid,l,mid,p<<1),Query(mid+1,R,mid+1,r,p<<1|1));
	}
	int find_l(int L,int R,int x,int p) { //需要吃的在右边 ,从左边找
		if(tree[p].mi>x)return L-1;
		if(L==R)return L;
		int mid=(L+R)>>1;
		if(tree[p<<1|1].mi<x)return find_l(mid+1,R,x,p<<1|1);
		else return find_l(L,mid,x,p<<1);
	}
	int find_r(int L,int R,int x,int p) {
		if(tree[p].mi>x)return R+1;
		if(L==R)return L;
		int mid=(L+R)>>1;
		if(tree[p<<1].mi<x)return find_r(L,mid,x,p<<1);
		else return find_r(mid+1,R,x,p<<1|1);
	}

} Tl,Tr;
char s[2];
int main() {
	int now=1;//用来记录当前最小的排名
	scanf("%d%d",&n,&K);
	for(int i=1; i<=n; i++)scanf("%d",&a[i].x),a[i].id=i;
	sort(a+1,a+n+1);
	int tot=0;
	for(int i=1; i<=n; i++)Rank[i]=a[i].id,Id[a[i].id]=i;//记录最初的排名
	if(K!=1)Tl.build(1,K-1,1);
	if(K!=n)Tr.build(K+1,n,1);
	scanf("%d",&q);
	while(q--) {
		scanf("%s",s);
		if(s[0]=='F') {
			int x;
			scanf("%d",&x);
			if(x==K)printf("0\n");
			else if(x>K) {
				int k=Tr.Query(K+1,n,K+1,x,1),l=0;
				if(K!=1)l=K-1-Tl.find_l(1,K-1,k,1);
				printf("%d\n",l+x-K);
			} else {
				int k=Tl.Query(1,K-1,x,K-1,1),r=0;
				if(K!=n)r=Tr.find_r(K+1,n,k,1)-K-1;
				printf("%d\n",r+K-x);
			}
		} else {
			int x,y,p=min(n,10);
			scanf("%d%d",&x,&y);
			for(int i=1; i<=min(n,10); i++)if(Rank[i]==x)p=i;//判断x原来的位置是不是在前十
			for(int i=p-1; i>=y; i--)Rank[i+1]=Rank[i];//更新前十的排名
			Rank[y]=x;
			for(int i=y; i>=1; i--) {
				now--;
				if(Rank[i]>K)Tr.updata(K+1,n,Rank[i],now,1);
				else if(Rank[i]<K)Tl.updata(1,K-1,Rank[i],now,1);
			}
		}
	}
	return 0;
}
posted @ 2019-07-19 14:41  季芊月  阅读(325)  评论(0编辑  收藏  举报