算法难度5,思维难度5,代码难度5
给定一个 n n n 排列,要求支持两个操作:
m m m 个操作之后,会询问位置 q q q 上的值。
1 ≤ n , m , q ≤ 1 0 5 1\le n,m,q\le 10^5 1≤n,m,q≤105
线段树分裂/合并的板题(
这题有个非常好写的做法,考虑二分答案。
二分答案mid之后,所有<=mid的数都变成0,所有>mid的数都变成1。
那么两种排序就可以用线段树区间修改来实现了。
最后去check位置 q q q 上的值是0还是1,就可以调整二分边界了。
O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
顺带一提,这题我试行了我的训练计划,在30min内完成了写暴力和正解,对拍。
下面提供了std,baoli和gen 。
// std
#include
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
inline int read(){
int x=0,f=1;char ch=' ';
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f==1?x:-x;
}
const int N=1e5+5;
int n,m,q;
int a[N],opt[N],L[N],R[N];
int sum[N<<2],cg[N<<2],Len[N<<2];
inline void pushup(int rt){sum[rt]=sum[rt<<1]+sum[rt<<1|1];}
inline void pushdown(int rt){
if(cg[rt]!=-1){
sum[rt<<1]=cg[rt]*Len[rt<<1];
cg[rt<<1]=cg[rt];
sum[rt<<1|1]=cg[rt]*Len[rt<<1|1];
cg[rt<<1|1]=cg[rt];
cg[rt]=-1;
}
}
inline void build(int rt,int l,int r,int v){
cg[rt]=-1;
Len[rt]=r-l+1;
if(l==r){
if(a[l]>v)sum[rt]=1;
else sum[rt]=0;
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid,v);
build(rt<<1|1,mid+1,r,v);
pushup(rt);
}
inline void modify(int rt,int l,int r,int L,int R,int v){
if(L<=l && r<=R){
cg[rt]=v;
sum[rt]=v*Len[rt];
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(L<=mid)modify(rt<<1,l,mid,L,R,v);
if(mid+1<=R)modify(rt<<1|1,mid+1,r,L,R,v);
pushup(rt);
}
inline int query(int rt,int l,int r,int L,int R){
if(L<=l && r<=R)return sum[rt];
pushdown(rt);
int mid=(l+r)>>1,ans;
if(L<=mid && mid+1<=R)ans=query(rt<<1,l,mid,L,R)+query(rt<<1|1,mid+1,r,L,R);
else if(L<=mid)ans=query(rt<<1,l,mid,L,R);
else ans=query(rt<<1|1,mid+1,r,L,R);
pushup(rt);
return ans;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=m;++i){opt[i]=read();L[i]=read();R[i]=read();}
q=read();
int l=0,r=n,mid;
while(l<r){
mid=(l+r)>>1;
build(1,1,n,mid);
for(int i=1;i<=m;++i){
int num=query(1,1,n,L[i],R[i]);
if(!opt[i]){
if(num)modify(1,1,n,R[i]-num+1,R[i],1);
if(num<R[i]-L[i]+1)modify(1,1,n,L[i],R[i]-num,0);
}
else{
if(num)modify(1,1,n,L[i],L[i]+num-1,1);
if(num<R[i]-L[i]+1)modify(1,1,n,L[i]+num,R[i],0);
}
}
if(query(1,1,n,q,q))l=mid+1;
else r=mid;
}
printf("%d\n",l);
return 0;
}
// baoli
#include
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
inline int read(){
int x=0,f=1;char ch=' ';
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f==1?x:-x;
}
const int N=1e5+5;
int n,m,q;
int a[N];
inline bool cmp(int x,int y){return x>y;}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=m;++i){
int opt=read(),l=read(),r=read();
if(!opt)sort(a+l,a+r+1);
else sort(a+l,a+r+1,cmp);
}
q=read();
printf("%d\n",a[q]);
return 0;
}
//gen
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
const int N=1005;
int a[N];
int main(){
srand(time(0));
int n=1000,m=1000;
printf("%d %d\n",n,m);
for(int i=1;i<=n;++i)a[i]=i;
random_shuffle(a+1,a+n+1);
for(int i=1;i<=n;++i)printf("%d ",a[i]);
putchar('\n');
for(int i=1;i<=m;++i){
int opt=rand()&1;
int l=rand()%n+1,r=rand()%n+1;
if(l>r)swap(l,r);
printf("%d %d %d\n",opt,l,r);
}
int q=rand()%n+1;
printf("%d\n",q);
return 0;
}