• 「CEOI2014」 Cake 题解


    题意

    给了一个长 \(n\) 序列和开始位置,从开始位置起,每次走到两边最小的一个。中间可以修改某个数的排名到前十以内,多次询问每个数在被走到之前会先走多少个数。

    题解

    本篇题解中,我们以起始位置为分界线,把整个序列分成两部分。

    不难发现每次我们的走法一定是走到某个地方,然后掉头,以此类推向两边扩展。而掉头的时候就是遇到了一个数,其值大于另一边的数的时候。可以发现在掉头之前,另一边的数是不会变化的。

    而且我们不难想到若能走到某个数,则该数到起始位置之间(不包括起始位置)之间所有的数都被走过了。当然,也不难想到最有价值的其实应该是最大的那一个,因为它最有可能在从另一边过来的时候有阻挡

    那么什么时候最大的这个数也会被走到呢?那就是另外一边被走完了或者另外一边有更大的数进行了阻挡,由于向另一边走时,这边的最大值是不变的,因此进行阻挡的就是另一边第一个会被走到的大于那个最大值的数。

    所以对于每个操作,我们需要维护:这个数到起始位置(不包括起始位置)之间的区间最大值某一部分最靠近起始位置的、大于某个数的数的位置以及 单点修改排名 之后的结果,很容易想到线段树,同时对第二个操作进行二分。

    至于略微有些恶心的排名,由于一开始这个序列中所有元素值都在 \(n\) 以内,因此我们设一个极大值为 \(n\) ,而题目保证了每次修改后的排名都在前十,所以暴力给前十的元素按排名改值就可以了。其实我猜出题人是出数据发现要保证序列中元素不重复懒得出了然后直接改成了改排名

    然后我们就能以 \(\mathcal{O(q \log n)}\) (再加上 \(10\) 的常数)通过这道题了。


    代码

    查看代码
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int n;
    int d[250005],arr[250005],rk[250005];
    struct SegmentTree{
    	#define ls p<<1
    	#define rs p<<1|1
    	#define lson p<<1,l,mid
    	#define rson p<<1|1,mid+1,r
    	int tr[1000005]; 
    	void pushUp(int p){tr[p]=max(tr[ls],tr[rs]);}
    	void build(int p,int l,int r)
    	{
    		if(l==r)
    		{
    			tr[p]=d[l];
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(lson);build(rson);
    		pushUp(p);
    		return;
    	}
    	void Modify(int p,int l,int r,int aim,int val)
    	{
    		if(l==r&&l==aim)
    		{
    			tr[p]=val;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(aim<=mid) Modify(lson,aim,val);
    		else Modify(rson,aim,val);
    		pushUp(p);
    	}
    	int queryMax(int p,int l,int r,int lx,int rx)
    	{
    		if(lx<=l&&r<=rx) return tr[p];
    		int mid=(l+r)>>1,ret=0;
    		if(lx<=mid) ret=max(ret,queryMax(lson,lx,rx));
    		if(mid<rx)  ret=max(ret,queryMax(rson,lx,rx));
    		return ret; 
    	}
    	int upper_bound_left(int p,int l,int r,int lx,int rx,int val)
    	{
    		if(l>rx||r<lx) return -1;
    		if(tr[p]<val) return -1;
    		if(l==r) return l;	
    		int mid=(l+r)>>1;
    		int rres=upper_bound_left(rson,lx,rx,val);
    		if(rres>=1) return rres;
    		else return upper_bound_left(lson,lx,rx,val);
    	}
    	int upper_bound_right(int p,int l,int r,int lx,int rx,int val)
    	{
    		if(l>rx||r<lx) return -1;
    		if(tr[p]<val) return -1;
    		if(l==r) return l;
    		int mid=(l+r)>>1;
    		int lres=upper_bound_right(lson,lx,rx,val);
    		if(lres>=1) return lres;
    		else return upper_bound_right(rson,lx,rx,val);
    	}
    	void Debug(int p,int l,int r)
    	{
    		int mid=(l+r)>>1;
    		printf("[%d,%d]:%d\n",l,r,tr[p]);
    		Debug(lson);Debug(rson);
    	}
    }Tr;
    char s[250005];
    int main() {
    	int a,m;
    	scanf("%d %d",&n,&a);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&d[i]);
    		if(n-d[i]+1<=10) rk[n-d[i]+1]=i;
    	}
    	Tr.build(1,1,n);
    	scanf("%d",&m);
    	int Maxn=n,pos,val;
    	while(m--)
    	{
    		scanf("%s",s+1);
    		if(s[1]=='E')
    		{
    			int lst=min(n,10);
    			scanf("%d %d",&pos,&val);
    			for(int i=1;i<=10;i++) if(rk[i]==pos) lst=i;
    			for(int i=lst-1;i>=val;i--) rk[i+1]=rk[i];
    			rk[val]=pos;
    			for(int i=val;i>=1;i--) Maxn++,Tr.Modify(1,1,n,rk[i],Maxn);
    		}
    		if(s[1]=='F')
    		{
    			scanf("%d",&pos);
    			if(pos==a)
    			{
    				puts("0");
    				continue;
    			}
    			if(pos<a)
    			{
    				int MAX=Tr.queryMax(1,1,n,pos,a-1);
    				int k=Tr.upper_bound_right(1,1,n,a+1,n,MAX);
    				if(k==-1) k=n+1;
    				printf("%d\n",k-pos-1);
    			}
    			else
    			{
    				int MAX=Tr.queryMax(1,1,n,a+1,pos);
    				int k=Tr.upper_bound_left(1,1,n,1,a-1,MAX);
    				if(k==-1) k=0;
    				printf("%d\n",pos-k-1);
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    恭喜你,你毕业了
    用VB.Net2008制作安装程序详细步骤(菜鸟级别,高手勿进)
    交通标志结构计算软件开发进程
    【工作笔记002】在TC中建立应用于出行分布的阻抗矩阵(最短路矩阵)
    VB.Net 2008 引用Excel12
    开博,开播。
    【推荐】万物兴歇——衰老与寿命的演化
    一张交叉口渠划的彩色平面图
    萦绕在头脑中的思路_我的编程梦们 【更新至2010.06.03】
    8月份的回顾
  • 原文地址:https://www.cnblogs.com/Azazel/p/14440103.html
Copyright © 2020-2023  润新知