1 条题解

  • 0
    @ 2025-8-24 22:24:57

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar jun头吉吉
    alive

    搬运于2025-08-24 22:24:57,当前版本为作者最后更新于2020-11-12 19:31:31,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题意

    一开始将一列人按编号从11开始顺序排列,然后进行AA次操作,每次将编号为AA的人拿出,放在编号为 BB 的人前面,所有操作进行完后有QQ个问题,询问编号为XX的人的位置,或者询问在XX号位置上的人的编号。

    2N50000,1A,B1092≤N≤50000,1≤A,B≤10^9

    1Q50000,1X1091≤Q≤50000,1≤X≤10^9

    题解

    定位:ds\rm ds简单题假装我没有调两天才调出来

    首先我们不去考虑数据范围,假设maxai,bi1000000\max{a_i,b_i}\le 1000000,那么该怎么做呢?

    观察操作:拿出一个数,在插到另一个数前面,可以用双向链表轻松解决。

    xx记录其前驱prepre与后继nxtnxt,那么要求删除可以看做把他的前驱与后继连起来,而插入就相当于在一个数和他的前驱之间加上一个数。十分简单,这里不过多赘述。

    最后只要找到前驱为00的数再不断往后找就可以复原这个序列了。

    然而CEOI没有这个部分分差评

    再考虑上述算法为什么慢¿显然对于110000000001\to 1000000000大部分数字的前驱与后继并没有改变,仍然是x1x-1x+1x+1,因此我们把所有的数分成两类:

    1. 前驱或后继更改过的
    2. 前驱与后继不变的

    显然,第11类的个数不会超过3n3n。那么我们可以用map\rm map来维护第11类值的前驱与后继,再把所有前驱或后继更改过的数字拿出来变成若干块。

    显然,只有红框里的数前驱与后继发生了变化。我们需要一种东西来找到并记录红框。

    如果我们用map\rm map来维护每一个值的前驱与后继,那么对于每一个块的开始,其前驱必然不在map\rm map中出现,我们可以利用这点性质找到每一个起始位置,并把每一段塞进vector\rm vector里。

    然后对于剩下的数,其必然是递增的,且可以看做是[1,100000000][1,100000000]删去第11类数。

    由于希望这些块按前后排序,我们对每个块记录整个块的前驱与后继,那么按前驱排序就把这些块前后排序了。

    那么对于每个块,我们可以容易地维护他前面的数字总数。两个块之间的数字数为preinxti1+1pre_i-nxt_{i-1}+1。那么对于块内的数,其位置与对相应位置的值也呼之欲出了。

    接下来就是不在块内的数:

    给位置求编号

    我们记录所有块结束时的数字总数,那么肯定能找到第一个块,使得sumixsum_i\le x,二分可以轻松解决。

    那么xx位置上的值,就是第 x再此之前的1类个数x-\text{再此之前的1类个数} 个第22类值。在此之前的11类个数可以前缀和维护。

    给编号求位置

    找到第一个块使得nxtixnxt_i\le x,二分一下就好了。

    然后xx的位置,就是x在第二类中的编号+再此之前的1类个数x\text{在第二类中的编号}+\text{再此之前的1类个数}。维护方式类似。

    于是最后只剩下一个问题:第22类数最多有10000000001000000000个,如何给数字求编号或给编号求数字。事实上,动态开点线段树可以解决。每个节点记录一个已经删去了多少个,再乱搞一下就好了。

    代码

    边界需要注意一下,其他也没什么了。

    #include<bits/stdc++.h>
    namespace in{
    	char buf[1<<21],*p1=buf,*p2=buf;
    	inline int getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    	template <typename T>inline void read(T& t){
    		t=0;int f=0;char ch=getc();while (!isdigit(ch)){if(ch=='-')f = 1;ch=getc();}
    	    while(isdigit(ch)){t=t*10+ch-48;ch = getc();}if(f)t=-t;
    	}
    	template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
    }
    namespace out{
    	char buffer[1<<21];int p1=-1;const int p2 = (1<<21)-1;
    	inline void flush(){fwrite(buffer,1,p1+1,stdout),p1=-1;}
    	inline void putc(const char &x) {if(p1==p2)flush();buffer[++p1]=x;}
    	template <typename T>void write(T x) {
    		static char buf[15];static int len=-1;if(x>=0){do{buf[++len]=x%10+48,x/=10;}while (x);}else{putc('-');do {buf[++len]=-(x%10)+48,x/=10;}while(x);}
    		while (len>=0)putc(buf[len]),--len;
    	}
    }
    using namespace std;
    int n,q;
    //双向链表部分
    struct node{
    	int pre,nxt;//记录前驱与后继 
    	node(){pre=nxt=0;}
    	node(int _pre,int _nxt){pre=_pre,nxt=_nxt;};
    }; 
    map<int,node>List;
    bool has(int x){
    	return List.find(x)!=List.end();
    }
    void chk(int x){
    	if(!has(x))
    		List[x]=node(x-1,x+1);
    }
    void connect(int x,int y){
    	List[x].nxt=y;
    	List[y].pre=x;
    }
    //维护块
    vector<int>num[100000+10];
    int cnt=0;
    struct node2{
    	int pre,nxt,id,sum,lensum;
    	node2(){id=cnt++;}
    	bool operator<(const node2 b)const{
    		return pre<b.pre;
    	}
    };
    vector<node2>block;
    //维护块内有关信息
    map<int,int>wei;//wei[x] 记录x的位置
    map<int,int>val;//val[x] 记录x上的值
    //动态开点线段树查询
    struct t{
    	int lc,rc;
    	int sz;
    }T[100000*32];
    int root,tot;
    #define mid (l+r>>1)
    void upd(int &x,int l,int r,int pos){
    	//把pos改为0
    	if(!x)x=++tot,T[x].sz=0;
    	if(l==r){T[x].sz=1;return;}
    	if(pos<=mid)upd(T[x].lc,l,mid,pos);
    	else upd(T[x].rc,mid+1,r,pos);
    	T[x].sz=T[T[x].lc].sz+T[T[x].rc].sz;
    	//printf("%d [%d,%d] %d\n",x,l,r,t[x].sz); 
    }
    int qry1(int x,int l,int r,int pos){
    	//printf("%d %d %d   sz:%d\n",x,l,r,T[x].sz);
    	//查询在小于等于pos的和
    	if(!x)return pos-l+1;
    	if(l==r)return 1-T[x].sz;
    	if(pos<=mid)return qry1(T[x].lc,l,mid,pos);
    	return (mid-l+1-T[T[x].lc].sz)+qry1(T[x].rc,mid+1,r,pos);
    }
    int qry2(int x,int l,int r,int pos){
    	//查询第 pos 个未被删除的值
    	if(!x){
    		//说明l-r都未被删除
    		return l+pos-1; 
    	}
    	int hasl=mid-l+1-T[T[x].lc].sz;
    	if(hasl>=pos)return qry2(T[x].lc,l,mid,pos);
    	else return qry2(T[x].rc,mid+1,r,pos-hasl);
    }
    signed main(){
        //freopen("1.in","r",stdin);
    	//freopen("1.out","w",stdout);
    	in::read(n);
    	for(int i=1,a,b;i<=n;i++){
    		in::read(a,b);
    		chk(a);chk(List[a].pre);chk(List[a].nxt);
    		chk(b);chk(List[b].pre);
    		connect(List[a].pre,List[a].nxt);
    		connect(List[b].pre,a);
    		connect(a,b);
    	}
    	List.erase(0);
    	for(auto k:List){
    		//printf("%d<- %d ->%d\n",k.second.pre,k.first,k.second.nxt); 
    		if(!has(k.second.pre)){
    			block.push_back(node2());
    			block.back().pre=k.second.pre;
    			int now=k.first;
    			while(has(now)){
    				num[block.back().id].push_back(now);
    				now=List[now].nxt;
    			}
    			block.back().nxt=now;
    		}
    	}
    	block.push_back(node2());
    	block.back().pre=-1000;
    	block.back().sum=0;
    	block.back().lensum=0;
    	
    	sort(block.begin(),block.end());
    	//for(auto k:block){
    	//	printf("%d %d\n",k.pre,k.nxt);
    	//	for(auto nn:num[k.id])
    	//		printf("%d ",nn);
    	//	printf("\n");
    	//}
    	
    	int lst=1,sum=0,lensum=0;//记录上一个块的后继 ,已经一共有的数
    	for(auto &k:block){
    		if(k.pre<0)continue;
    		sum+=k.pre-lst+1;
    		lst=k.nxt;
    		for(auto nn:num[k.id])
    			sum++,wei[nn]=sum,val[sum]=nn,
    			upd(root,1,1000000000,nn);
    		k.sum=sum;
    		k.lensum=lensum+num[k.id].size();
    		lensum=k.lensum;
    	} 
    	//for(auto k:wei)
    	//	printf("%d在%d\n",k.first,k.second);
    	in::read(q);
    	while(q--){
    		char op=in::getc();int x;
    		while(!isalpha(op))op=in::getc();
    		in::read(x);
    		if(op=='P'){
    			if(wei[x])
    				out::write(wei[x]);
    			else{
    				int l=0,r=block.size()-1;
    				int ans=0;
    				while(l<=r){
    					if(block[mid].nxt<=x)
    						ans=mid,l=mid+1;
    					else r=mid-1;
    				}
    				out::write(block[ans].lensum+qry1(root,1,1000000000,x));
    			}
    		}else{
    			if(val[x])
    				out::write(val[x]);
    			else{
    				int l=0,r=block.size()-1;
    				int ans; 
    				while(l<=r){
    					if(block[mid].sum<=x)
    						ans=mid,l=mid+1;
    					else r=mid-1;
    				}
    				out::write(qry2(root,1,1000000000,x-block[ans].lensum));
    			}
    		}
    		out::putc('\n');
    		
    	}
    	out::flush();
    	return 0;
    }
    
    • 1

    信息

    ID
    5889
    时间
    3000ms
    内存
    500MiB
    难度
    (无)
    标签
    递交数
    0
    已通过
    0
    上传者