[CDQ分治][树状数组][树套树] Jzoj P3197 K大数查询

Posted comfortable

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CDQ分治][树状数组][树套树] Jzoj P3197 K大数查询相关的知识,希望对你有一定的参考价值。

Description

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

Input


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

Output

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

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
 

Data Constraint


30%的数据n=m=1000
100%的数据n,m≤50000,并且后7 个点的数据n,m 的范围从32000 到50000近似成等差数列递增。a≤b≤n,1 操作中|c|≤n,2 操作中|c|≤maxlongint
 

Hint

第一个操作后位置1 的数只有1,位置2 的数也只有1。第二个操作后位置1的数有1、2,位置2 的数也有1、2。第三次询问位置1 到位置1 第2 大的数是1。第四次询问位置1 到位置1 第1 大的数是2。第五次询问位置1 到位置2 第3大的数是1。

 

题解

  • 题目简洁大方,好评!!!
  • 这种题一般都是用树套树来做滴,题目大意:要求支持区间修改,区间查询第K大
  • 考虑一下CDQ分治,先二分一个答案
  • 我们就对于一下当前这一段的处理序列中,先依次处理,碰到询问就考虑是否可行
  • 如果对于一个询问,发现当前的x之下查询的ans大于那个值,说明答案更小,所以要放到左边去递归处理
  • 但是同时记得,把询问的值减掉查询出的ans,表示这一段肯定比它大,先减掉
  • 至于查询的话,区间修改、区间查询,可以用树状数组就行了

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #define ll long long
 5 #define N 50010
 6 using namespace std;
 7 int n,m,ans[N];
 8 ll sz1[N],sz2[N];
 9 bool bz[N];
10 struct edge{ int d,x,y,c,op; }a[N],P[N],Q[N];
11 void add(int x,int y) { for (int r=x;x<=n;x+=x&-x) sz1[x]+=y,sz2[x]+=r*y; }
12 ll query(int x)
13 {
14     ll r=0;
15     for (int i=x;i;i-=i&-i) r+=(x+1)*sz1[i]-sz2[i];
16     return r;
17 }
18 void cdq(int L,int R,int l,int r)
19 {
20     if (L>R||l>r) return;
21     if (l==r) 
22     {
23         for (int i=L;i<=R;i++) ans[a[i].d]=l;
24         return;
25     }
26     int mid=l+r+1>>1,num1=0,num2=0;ll x;
27     for (int i=L;i<=R;i++)
28         if (a[i].op==1)
29         {
30             if (a[i].c>=mid) add(a[i].x,1),add(a[i].y+1,-1),P[++num2]=a[i]; else Q[++num1]=a[i];
31         }
32         else
33         {
34             x=query(a[i].y)-query(a[i].x-1);
35             if (x>=a[i].c) P[++num2]=a[i]; else a[i].c-=x,Q[++num1]=a[i];
36         }
37     for (int i=L;i<=R;i++) if (a[i].op==1&&a[i].c>=mid) add(a[i].x,-1),add(a[i].y+1,1);
38     for (int i=1;i<=num1;i++) a[L+i-1]=Q[i];
39     for (int i=1;i<=num2;i++) a[L+num1+i-1]=P[i];
40     cdq(L,L+num1-1,l,mid-1),cdq(L+num1,R,mid,r);
41 }
42 int main()
43 {
44     scanf("%d%d",&n,&m);
45     for (int i=1;i<=m;i++)
46     {
47         scanf("%d%d%d%d",&a[i].op,&a[i].x,&a[i].y,&a[i].c),a[i].d=i;
48         if (a[i].op==2) bz[i]=1;
49     }
50     cdq(1,m,1,n);
51     for (int i=1;i<=m;i++) if (bz[i]) printf("%d
",ans[i]);
52 }

 

以上是关于[CDQ分治][树状数组][树套树] Jzoj P3197 K大数查询的主要内容,如果未能解决你的问题,请参考以下文章

「APIO2019」路灯 (K-D Tree / 树套树 / CDQ + 树状数组)

[填坑]一直遗忘的树套树版本动态CDQ

Cideforces 1093E Intersection of Permutations (CDQ分治+树状数组)

bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

BZOJ3262陌上花开(CDQ分治)

[BZOJ3295][Cqoi2011]动态逆序对 CDQ分治&树套树