题解 P6887 【[CEOI2006]Queue】
题意
一开始将一列人按编号从
题解
定位:假装我没有调两天才调出来
首先我们不去考虑数据范围,假设
观察操作:拿出一个数,在插到另一个数前面,可以用双向链表轻松解决。
为
最后只要找到前驱为
然而CEOI没有这个部分分差评
再考虑上述算法为什么慢¿显然对于
- 前驱或后继更改过的
- 前驱与后继不变的
显然,第
显然,只有红框里的数前驱与后继发生了变化。我们需要一种东西来找到并记录红框。
如果我们用
然后对于剩下的数,其必然是递增的,且可以看做是
由于希望这些块按前后排序,我们对每个块记录整个块的前驱与后继,那么按前驱排序就把这些块前后排序了。
那么对于每个块,我们可以容易地维护他前面的数字总数。两个块之间的数字数为
接下来就是不在块内的数:
给位置求编号
我们记录所有块结束时的数字总数,那么肯定能找到第一个块,使得
那么
给编号求位置
找到第一个块使得
然后
于是最后只剩下一个问题:第
代码
边界需要注意一下,其他也没什么了。
#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;
}