[ZJOI2013]K大数查询

Description

N 个位置,M个操作。操作有两种,每次操作如果是 1 a b c 的形式表示在第 a 个位置到第b个位置,每个位置加入一个数 c
如果是2 a b c形式,表示询问从第 a 个位置到第b个位置,第 C 大的数是多少。

Input

第一行N M
接下来M行,每行形如 1 a b c 2 a b c

Output

输出每个询问的结果。

Sample Input

2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3

Sample Output

1
2
1

HINT

【样例说明】

第一个操作后位置 1 的数只有1,位置 2 的数也只有1。 第二个操作后位置 1 的数有1 2 ,位置2的数也有 1 2。第三次询问位置 1 到位置1 2 大的数是1。第四次询问位置 1 到位置1 1 大的数是2。第五次询问位置 1 到位置2 3 大的数是1。‍
N,M<=50000,N,M<=50000
a<=b<=N
1 操作中|c|<=N
2 操作中c<=Maxlongint

Source

思路

树套数,权值线段树套位置线段树。
如图:

这个图表示 1 号位置有一个数1 2 号位置有一个数2
首先对纵坐标建立一棵线段树(权值线段树);
再在权值线段树的每一个节点上对横坐标建立一棵线段树(位置线段树);
插入操作( 1 a b c )就是找到权值线段树上包含 c 这个点的区间,并将这些区间的线段树上[a,b]这个区间加上 1
查询操作(2 a b c)就是在权值线段树上查找对于 [a,b] 区间,刚好有 [x,n]c (x,n]>c 的满足条件的 x <script type="math/tex" id="MathJax-Element-3598">x</script>

代码

19356ms
(超大常数和超级扩行)

#include <cstdio>

const int maxn=50000;
const int maxs=20000000;

int n;

struct pos_segment_tree//位置线段树
{
  int ls[maxs],rs[maxs],cnt;
  unsigned val[maxs],lazy[maxs];

  inline int newnode()//为了节省空间,每访问一个节点再新建一个节点
  {
    ++cnt;
    ls[cnt]=rs[cnt]=val[cnt]=lazy[cnt]=0;
    return cnt;
  }

  inline int pushdown(int now,int left,int right)
  {
    if((!lazy[now])||(left==right))
      {
        return 0;
      }
    if(!ls[now])
      {
        ls[now]=newnode();
      }
    if(!rs[now])
      {
        rs[now]=newnode();
      }
    int mid=(left+right)>>1;
    val[ls[now]]+=lazy[now]*(mid-left+1);
    val[rs[now]]+=lazy[now]*(right-mid);
    lazy[ls[now]]+=lazy[now];
    lazy[rs[now]]+=lazy[now];
    lazy[now]=0;
    return 0;
  }

  inline unsigned updata(int now)
  {
    return val[now]=val[ls[now]]+val[rs[now]];
  }

  int add(int &now,int left,int right,int askl,int askr)
  {
    if(!now)
      {
        now=newnode();
      }
    pushdown(now,left,right);
    if((askl<=left)&&(right<=askr))
      {
        ++lazy[now];
        val[now]+=right-left+1;
        return 0;
      }
    int mid=(left+right)>>1;
    if(askl<=mid)
      {
        add(ls[now],left,mid,askl,askr);
      }
    if(mid<askr)
      {
        add(rs[now],mid+1,right,askl,askr);
      }
    updata(now);
    return 0;
  }

  unsigned query(int now,int left,int right,int askl,int askr)
  {
    if(!now)
      {
        return 0;
      }
    pushdown(now,left,right);
    if((askl<=left)&&(right<=askr))
      {
        return val[now];
      }
    int mid=(left+right)>>1;
    unsigned int res=0;
    if(askl<=mid)
      {
        res+=query(ls[now],left,mid,askl,askr);
      }
    if(mid<askr)
      {
        res+=query(rs[now],mid+1,right,askl,askr);
      }
    return res;
  }
};

struct val_segment_tree//权值线段树
{
  int root[maxn<<4];
  pos_segment_tree pst;

  int add(int now,int left,int right,int askl,int askr,int cv)
  {
    pst.add(root[now],1,n,askl,askr);
    if(left==right)
      {
        return 0;
      }
    int mid=(left+right)>>1;
    if(cv<=mid)
      {
        add(now<<1,left,mid,askl,askr,cv);
      }
    else
      {
        add(now<<1|1,mid+1,right,askl,askr,cv);
      }
    return 0;
  }

  int query(int now,int left,int right,int pl,int pr,unsigned int k)
  {
    if(left==right)
      {
        return left;
      }
    int mid=(left+right)>>1;
    unsigned int eq=pst.query(root[now<<1],1,n,pl,pr);
    if(eq>=k)
      {
        return query(now<<1,left,mid,pl,pr,k);
      }
    else
      {
        return query(now<<1|1,mid+1,right,pl,pr,k-eq);
      }
  }
};

val_segment_tree vst;
int m,opt,a,b,c;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值