人,只有自己站起来,这个世界才能属于他。|

园龄:粉丝:关注:

题解:P6544 [CEOI2014] Cake

P6544 解题报告

前言

感觉没有紫……

但是评了我也没意见。

思路分析

首先考虑怎样计算答案。

如果 ba 的左侧,那么设 x[b,a] 的最大值,c[a,n] 中最靠左的 dc>x 的位置,那么答案为 bc1

应该不难理解。想要吃掉 b,必须吃掉 [b,a] 的所有蛋糕,和 [a,n] 中部分比 db 小的蛋糕。

可以直接线段树二分解决。

瓶颈在于修改。

发现这个修改非常神秘,它只修改值的大小关系,但是不修改具体的值。

因为 e10 非常不正常,所以考虑从这里入手解决。

发现如果我们只维护前 10 大所在的位置,那么修改时可以把前 e1 大的值整体向前平移一段距离,给第 e 大留出位置,然后把第 e+1 大往后的排名整体后移。

感觉上比维护实数值好写多了。

然后就做完了。

实际上,只需要维护一棵支持单点修改,查询最大值,查询区间 >k 的最靠左/右的下标的线段树就行了。

总体复杂度 O(enlogn),在洛谷上跑进了 300 ms。

代码实现

感觉上不是很难写,但是从想到做完还是用了 1 h。


#include<bits/stdc++.h>
using namespace std;
int n,m,a,pos,maxn,sum,num,id,e,d[250005],c[250005];
char op;
int val[500005],ls[500005],rs[500005],dcnt,rt;
void pushup(int x){
	val[x]=max(val[ls[x]],val[rs[x]]);
}
void build(int l,int r,int &x){
	x=++dcnt;
	if(l==r){
		val[x]=d[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,ls[x]);
	build(mid+1,r,rs[x]);
	pushup(x);
}
void modify(int l,int r,int pos,int k,int x){
	if(l==r && l==pos){
		val[x]=k;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) modify(l,mid,pos,k,ls[x]);
	else modify(mid+1,r,pos,k,rs[x]);
	pushup(x);
}
int query(int l,int r,int ql,int qr,int x){
	if(ql<=l && r<=qr){
		return val[x];
	}
	int mid=(l+r)>>1,ans=0;
	if(ql<=mid) ans=max(ans,query(l,mid,ql,qr,ls[x]));
	if(qr>=mid+1) ans=max(ans,query(mid+1,r,ql,qr,rs[x]));
	return ans; 
}
int find_l(int l,int r,int ql,int qr,int k,int x){
	if(l==r){
		if(val[x]<k) return -1;
		else return l;
	}
	int mid=(l+r)>>1;
	if(ql<=mid && val[ls[x]]>=k){
		int ans=find_l(l,mid,ql,qr,k,ls[x]);
		if(ans!=-1) return ans;
	}
	if(qr>=mid+1){
		return find_l(mid+1,r,ql,qr,k,rs[x]);
	}else return -1;
}
int find_r(int l,int r,int ql,int qr,int k,int x){
	if(l==r){
		if(val[x]<k) return -1;
		else return l;
	} 
	int mid=(l+r)>>1;
	if(qr>=mid+1 && val[rs[x]]>=k){
		int ans=find_r(mid+1,r,ql,qr,k,rs[x]);
		if(ans!=-1) return ans;
	}
	if(ql<=mid){
		return find_r(l,mid,ql,qr,k,ls[x]);
	}else return  -1;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>a;
	for(int i=1;i<=n;i++){
		cin>>d[i];
		if(n-d[i]+1<=10) c[n-d[i]+1]=i;
	}
	build(1,n,rt);
	cin>>m;
	sum=n;
	for(int i=1;i<=m;i++){
		cin>>op;
		if(op=='F'){
			cin>>pos;
			if(pos==a){
				cout<<0<<'\n';
			}else if(pos<a){
				maxn=query(1,n,pos,a-1,rt);
				int k=(a==n?-1:find_l(1,n,a+1,n,maxn,rt));
				if(k==-1) k=n+1;
				cout<<k-pos-1<<'\n';
			}else{
				maxn=query(1,n,a+1,pos,rt);
				int k=(a==1?-1:find_r(1,n,1,a-1,maxn,rt));
				if(k==-1) k=0;
				cout<<pos-k-1<<'\n';
			}
		}else{
			int lst=min(n,10);
			cin>>pos>>num;
			for(int i=1;i<=10;i++){
				if(c[i]==pos) lst=i;
			}
			for(int i=lst-1;i>=num;i--){
				c[i+1]=c[i];
			}
			c[num]=pos;
			for(int i=num;i>=1;i--){
				sum++;
				modify(1,n,c[i],sum,rt);
			}
		}
	}
	return 0;
}


后记

最后要谴责 nfls 机子慢还要开小时限,使某些人的正解被卡常了。

还有我不是高二的老年选手……

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18689770

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(3)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起