题意:
给你一个序列,它是1~n的全排列,现在进行两种操作:
0 l r:把[l,r]从小到大排列;
1 l r:把[l,r]从大到小排列。
做完这些操作以后,请输出下标为k的数。
思路:
这道题挺巧妙的。
考虑到这只有一次查询操作,而且它是全排列的,所以可考虑二分,为什么可以二分呢?二分有什么用呢?我们后面来讲。
首先二分一个数mid(1~n),然后对于序列中小于它的数记为0,大于等于它的数记为1。
然后开始模拟操作了,这里用线段树实现:
对于升序操作:首先查询这个区间有多少个1,假设为x,然后把区间的前面部分修改为0,后面x个修改为1。
对于降序操作:首先查询这个区间有多少个1,假设为x,然后把区间的前面x个修改为1,后面部分修改为0。
做完这些操作以后,查询第k个位置是否是1,如果是1的话,说明第k个位置的数肯定是属于[mid,r]的,如果不是1的话,说明第k个位置的数是属于[1,mid-1]的,然后进行二分调整就可以了。
所以这棵线段树维护的是区间1的个数(即区间和)。
那么为什么可以二分呢?
这个二分更像是一种缩小范围的方式,我前面解释的时候就已经体现出来了,而且因为这个下标为k的数肯定是唯一的,所以肯定是可以找得到的。
再感叹一句,真的巧妙啊。
代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<set>
#include<bitset>
#include<map>
#include<vector>
#include<algorithm>
#define MOD 1000000007
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int n,m,num,ans;
int a[100010];
struct node
{
int left,right;
int val;//区间1的数量
int f;
}node[100010<<2];
int op[100000][3];
void build(int l,int r,int id,int x)
{
node[id].left=l;
node[id].right=r;
node[id].f=-1;
if(l==r)
{
if(a[num]>=x)node[id].val=1;
else node[id].val=0;
//printf("%d ",node[id].val);
num++;
return;
}
int m=(l+r)>>1;
build(l,m,id<<1,x);
build(m+1,r,id<<1|1,x);
node[id].val=node[id<<1].val+node[id<<1|1].val;
}
void down(int id)
{
int l1=node[id<<1].left;
int r1=node[id<<1].right;
int l2=node[id<<1|1].left;
int r2=node[id<<1|1].right;
node[id<<1].f=node[id].f;
node[id<<1].val=(r1-l1+1)*node[id].f;
node[id<<1|1].f=node[id].f;
node[id<<1|1].val=(r2-l2+1)*node[id].f;
node[id].f=-1;
}
void query(int L,int R,int id)
{
int l=node[id].left;
int r=node[id].right;
if(l>=L&&r<=R)
{
ans+=node[id].val;
//printf("%d\n",node[id].val);
//if(L==2&&R==4)printf("[%d,%d]\n",l,r);
return;
}
if(node[id].f!=-1)down(id);
int m=(l+r)>>1;
if(L<=m)query(L,R,id<<1);
if(R>m)query(L,R,id<<1|1);
}
void update(int L,int R,int id,int x)
{
int l=node[id].left;
int r=node[id].right;
if(l>=L&&r<=R)
{
node[id].val=(r-l+1)*x;
node[id].f=x;
return;
}
if(node[id].f!=-1)down(id);
int m=(l+r)/2;
if(L<=m)update(L,R,id<<1,x);
if(R>m)update(L,R,id<<1|1,x);
node[id].val=node[id<<1].val+node[id<<1|1].val;
}
int judge(int x,int k)
{
num=1;
build(1,n,1,x);
//printf("\n");
for(int i=1;i<=m;i++)
{
if(op[i][0]==0)//升序
{
ans=0;
query(op[i][1],op[i][2],1);
//printf("q%d:%d\n",i,ans);
update(op[i][1],op[i][2]-ans,1,0);
//printf("(%d,%d)\n",op[i][1],op[i][2]-ans);
update(op[i][2]-ans+1,op[i][2],1,1);
//printf("(%d,%d)\n",op[i][2]-ans+1,op[i][2]);
}
else if(op[i][0]==1)
{
ans=0;
query(op[i][1],op[i][2],1);
//printf("q%d:%d\n",i,ans);
update(op[i][1],op[i][1]+ans-1,1,1);
//printf("(%d,%d)\n",op[i][1],op[i][1]+ans-1);
update(op[i][1]+ans,op[i][2],1,0);
//printf("(%d,%d)\n",op[i][1]+ans,op[i][2]);
}
}
ans=0;
query(k,k,1);
//printf("ans=%d\n",ans);
return ans;
}
int main()
{
int t,k;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&op[i][0],&op[i][1],&op[i][2]);
scanf("%d",&k);
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
//printf("mid=%d\n",mid);
if(judge(mid,k))l=mid+1;
else r=mid-1;
}
printf("%d\n",l-1);
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!