K大数查询

K大数查询

时间限制: 2 Sec 内存限制: 512 MB

题目描述
有n 个位置和m 个操作。操作有两种,每次操作如果是1 a b c 的形式,表 示往第a 个位置到第b 个位置每个位置加入一个数c。如果操作形如2 a b c 的形 式,表示询问从第a 个位置到第b 个位置,第c 大的数是多少。

输入
在输入文件sequence.in 中,第一行两个数n,m。
意义如题目描述。 接下来m 行每行形如1 a b c 或者2 a b c 如题目描述。

输出
在输出文件sequence.out 中,对于每个询问回答k 大数是多少。

样例输入
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3

样例输出
1
2
1

提示
第一个操作后位置1 的数只有1,位置2 的数也只有1。第二个操作后位置1 的数有1、2,位置2 的数也有1、2。第三次询问位置1 到位置1 第2 大的数是 1。第四次询问位置1 到位置1 第1 大的数是2。第五次询问位置1 到位置2 第3 大的数是1。
30%的数据n=m=1000
100%的数据n,m≤50000,并且后7 个点的数据n,m 的范围从32000 到50000

来源
zjoi2013

题解

带区间修改的要求区间第K大,使用树状数组套主席树解决。
在权值线段树上存2个值,一个s1表示每个区间被加了多少次,另一个s2表示每次修改操作左区间的总和。则size=s1*(x+1)-s2。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define N 50010
#define ll long long
using namespace std;
int n,m,t1,t2,cnt,wbs[20],cyc[20],rt[N];
struct node{int lc,rc,s1,s2;}t[N*50];

class seg_tree
{
  public:
  void modify(int &x,int pre,int l,int r,int des,int val,int num)
  {
    x=pre?pre:++cnt;
    t[x]=t[pre];t[x].s1+=val;t[x].s2+=num;
    if(l==r)return;
    int mid=l+r>>1;
    if(des<=mid)modify(t[x].lc,t[pre].lc,l,mid,des,val,num);
    else modify(t[x].rc,t[pre].rc,mid+1,r,des,val,num);
  }
  int qry(int x,int pre,int l,int r,int k,int L,int R)
  {
    if(l==r)return l;
    int cnt1=0,cnt2=0,mid=l+r>>1;
    for(int i=1;i<=t1;i++)cnt1+=R*t[t[wbs[i]].rc].s1-t[t[wbs[i]].rc].s2;
    for(int i=1;i<=t2;i++)cnt2+=L*t[t[cyc[i]].rc].s1-t[t[cyc[i]].rc].s2;
    if(cnt1-cnt2>=k)
    {
      for(int i=1;i<=t1;i++)wbs[i]=t[wbs[i]].rc;
      for(int i=1;i<=t2;i++)cyc[i]=t[cyc[i]].rc;
      return qry(t[x].rc,t[pre].rc,mid+1,r,k,L,R);
    }
    for(int i=1;i<=t1;i++)wbs[i]=t[wbs[i]].lc;
    for(int i=1;i<=t2;i++)cyc[i]=t[cyc[i]].lc;
    return qry(t[x].lc,t[pre].lc,l,mid,k-cnt1+cnt2,L,R);
  }
}T2;

class bit
{
  public:
  void modify(int x,int des,int val,int num)
  {
    for(;x<=n;x+=x&-x)T2.modify(rt[x],rt[x],1,n,des,val,num);
  }
  int qry(int l,int r,int k)
  {
    int x;t1=0;t2=0;
    x=r;for(;x;x-=x&-x)wbs[++t1]=rt[x];
    x=l;for(;x;x-=x&-x)cyc[++t2]=rt[x];
    return T2.qry(rt[r],rt[l],1,n,k,l+1,r+1);
  }
}T1;

int main()
{
  int type,a,b,c;
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++)
  {
    scanf("%d%d%d%d",&type,&a,&b,&c);
    if(type==1)T1.modify(a,c,1,a),T1.modify(b,c,-1,-b-1);
    else printf("%d\n",T1.qry(a-1,b,c));
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值