51nod 1981 如何愉快地与STL玩耍
Description
驴蛋蛋在愉快地与STL玩耍
突然间小A跳了出来对驴蛋蛋说,看你与STL玩的很开心啊,那我给你一个大小为N的vector,这个vector上每个位置上是一个set
每次我会在闭区间 [L,R] 中的每个set
小A走了,留下驴蛋蛋一个人外加一个长度为 N 的vector<set
根据小A的 ℂ++𝟚.𝟛𝟛 标准vector可以被视作一个数组,下标从 1 开始,大小不得超过 65536
set
小A最多会进行 Q 次(不超过 65536 )完全符合 ℂ++𝟚.𝟛𝟛 标准的操作,但如果出现询问时 [L,R] 区间里不足 K 个数的情况,你只需要对小A回答 −1 就好了
Solution
听说可以线段树+bitset直接水过,也可以整体二分做
和普通的整体二分唯一的不同就是就是需要去重
有一个套路:两个区间有交,可以写成 \(R>=l\) && \(r>=L\) 的形式
也就是说我们把修改映射成平面上的点 \((r,l)\),那么查询就是问 \((L,R)\) 右下角的点数之和
考虑去重:
假设两个点 \((x,y)\) 的权值相同
1.如果 \(x\) 在 \(y\) 的右下方,\(x\) 包含了 \(y\),那么 \(y\) 就没有用了,可以直接去掉
2.如果不是包含关系,只是普通的相交关系,那么就容斥一下即可,像下面这个图一样,给每一个一个权值,改为查询权值和
这个东西可以对每一个权值开一个 \(set\) 来维护
#include<bits/stdc++.h>
using namespace std;
const int N=80005;
int n,Q,cnt=0,rt[N],ans[N],DEP=0,c[N],ct[N];
struct sub{
int op,l,r,k,id;
bool operator <(const sub &p)const{return r<p.r;}
}q[N],A[N],B[N];
set<sub>S[N];
struct node{int ls,rs,w;}tr[N*100];
inline void ins(int &x,int l,int r,int sa,int t){
if(!x)x=++cnt;tr[x].w+=t;
if(l==r)return ;
int mid=(l+r)>>1;
if(sa<=mid)ins(tr[x].ls,l,mid,sa,t);
else ins(tr[x].rs,mid+1,r,sa,t);
}
inline void add(int sta,int p,int t){
for(int i=sta;i;i-=(i&(-i))){
if(ct[i]!=DEP)ct[i]=DEP,rt[i]=0;
ins(rt[i],1,n,p,t);
}
}
inline int qry(int x,int l,int r,int sa,int se){
if(!x)return 0;
if(sa<=l && r<=se)return tr[x].w;
int mid=(l+r)>>1;
if(se<=mid)return qry(tr[x].ls,l,mid,sa,se);
else if(sa>mid)return qry(tr[x].rs,mid+1,r,sa,se);
else return qry(tr[x].ls,l,mid,sa,mid)+qry(tr[x].rs,mid+1,r,mid+1,se);
}
inline int query(int sta,int p){
int ret=0;
for(int i=sta;i<=n;i+=(i&(-i)))if(ct[i]==DEP)ret+=qry(rt[i],1,n,1,p);
return ret;
}
inline void insert(sub t){
int k=t.k;
if(c[k]!=DEP)c[k]=DEP,S[k].clear();
if(S[k].empty()){add(t.r,t.l,1),S[k].insert(t);return ;}
set<sub>::iterator i,j,x;
i=S[k].upper_bound(t);
if(i!=S[k].end() && i->l<=t.l)return ;
if(i==S[k].begin()){
S[k].insert(t);
add(t.r,t.l,1);add(t.r,i->l,-1);
return ;
}
j=i;j--;
if(j->r==t.r && j->l<=t.l)return ;
if(i!=S[k].end())add(j->r,i->l,1),add(t.r,i->l,-1);
add(t.r,t.l,1);
while(j->l>=t.l){
add(j->r,j->l,-1);
if(j!=S[k].begin()){
x=j;--x;
add(x->r,j->l,1);
S[k].erase(j);j=x;
}
else{S[k].erase(j);j=S[k].end();break;}
}
if(j!=S[k].end())add(j->r,t.l,-1);S[k].insert(t);
}
inline void solve(int l,int r,int L,int R){
if(l==r){
for(int i=L,t=l>10000?-1:l;i<=R;i++)
if(q[i].op==1)ans[q[i].id]=-2;
else ans[q[i].id]=q[i].k?t:-1;
return ;
}
for(int i=1;i<=cnt;i++)tr[i].ls=tr[i].rs=tr[i].w=0;cnt=0;
int mid=(l+r)>>1;DEP++;
int xl=0,xr=0,p=L-1,t;
for(int i=L;i<=R;i++){
if(q[i].op==1){
if(q[i].k<=mid)A[++xl]=q[i],insert(q[i]);
else B[++xr]=q[i];
}
else if((t=query(q[i].l,q[i].r))>=q[i].k)A[++xl]=q[i];
else q[i].k-=t,B[++xr]=q[i];
}
for(int i=1;i<=xl;i++)q[++p]=A[i];
for(int i=1;i<=xr;i++)q[++p]=B[i];
if(xl)solve(l,mid,L,L+xl-1);if(xr)solve(mid+1,r,R-xr+1,R);
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
scanf("%d%d",&n,&Q);
for(int i=1;i<=Q;i++){
scanf("%d%d%d%d",&q[i].op,&q[i].l,&q[i].r,&q[i].k);
q[i].id=i;ans[i]=-1;
}
solve(0,10001,1,Q);
for(int i=1;i<=Q;i++)if(ans[i]!=-2)printf("%d\n",ans[i]);
return 0;
}