您的位置:首页 > 理论基础 > 数据结构算法

Cdqz_Openjudge1010 Challenge 10 解题报告【数据结构】【分块】

2017-08-09 15:57 681 查看
描述

给一个长为N的数列,有M次操作,每次操作是以下两种之一:

(1)修改数列中的一个数

(2)求数列中第K小的值

输入

第一行两个正整数N和M。

第二行N的整数表示这个数列。

接下来M行,每行开头是一个字符,若该字符为’M’,则表示一个修改操作,接下来两个整数x和y,表示把x位置的数修改为y;若该字符为’Q’,则表示一个询问操作,接下来一个整数x,表示求数列中第x小的值。

输出

对每一个询问操作单独输出一行,表示答案。

样例输入

5 3

1 2 3 4 5

Q 2

M 2 4

Q 2

样例输出

2

3

提示

1<=N<=10^5,1<=M<=10^5,输入保证合法,且所有整数可用带符号32位整型存储。

解题报告

这样的题我们做过很多遍了,可以参考这篇博客

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5;
int T;
int n,m,blk,cnt;
int a[N+5],b[N+5],b1[N+5],L[N+5],R[N+5];
void reset(int x)
{
for(int i=L[x];i<=R[x];i++)b[i]=a[i];
sort(b+L[x],b+1+R[x]);
}
void modify(int pos,int val)
{
a[pos]=val;
reset(b1[pos]);
}
int query(int l,int r,int k)
{
int lf=b1[l],rg=b1[r];
int ans=0;
if(lf==rg)//在一个块中直接暴力
{
for(int i=l;i<=r;i++)if(a[i]<=k)ans++;
}
else
{
for(int i=l;i<=R[lf];i++)if(a[i]<=k)ans++;
for(int i=L[rg];i<=r;i++)if(a[i]<=k)ans++;//左右不完全在一个块里的零头,暴力枚举
for(int i=lf+1;i<=rg-1;i++)
ans+=upper_bound(b+L[i],b+R[i]+1,k)-(b+L[i]);//比k大的灾块中的具体排名
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
blk=sqrt(n);
if(n%blk)cnt=n/blk+1;
else cnt=n/blk;
for(int i=1;i<=n;i++)b1[i]=(i-1)/blk+1;//i所属的那个块的序号
for(int i=1;i<=cnt;i++)L[i]=(i-1)*blk+1,R[i]=i*blk;//一个块的左右下标
R[cnt]=n;
for(int i=1;i<=cnt;i++)reset(i);
char ch;
while(m--)
{

scanf(" %c",&ch);
if(ch=='Q')
{
int k;
scanf("%d",&k);
int l=1,r=1e9,res;
while(l<=r)//二分答案
{
int mid=(l+r)>>1;
if(query(1,n,mid)>=k)r=mid-1,res=mid;
else l=mid+1;
}
printf("%d\n",res);
}
else
{
int pos,val;
scanf("%d%d",&pos,&val);
modify(pos,val);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐