[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 51 1 2 11 1 2 22 1 1 22 1 1 12 1 2 3

Sample Output

121

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\n",ans[i]);
52 }

原文地址:https://www.cnblogs.com/Comfortable/p/10335863.html

时间: 2024-08-26 08:50:54

[CDQ分治][树状数组][树套树] Jzoj P3197 K大数查询的相关文章

ZOJ 2112 Dynamic Rankings(主席树套树状数组+静态主席树)

题意:给定一个区间,求这个区间第k大的数,支持单点修改. 思路:主席树真是个神奇的东西.........速度很快但是也有一个问题就是占用内存的很大,一般来说支持单点修改的主席树套树状数组空间复杂度为O(n*logn*logn), 如果查询较少的话,可以初始的时候用一颗静态主席树,这样空间复杂度可以降为O(n*logn+q*logn*logn),勉强可以过zoj这道题. 这道题看了好久好久才懂...网上题解一般就几句话.......下面是自己的一些理解. 首先静态主席树这个东西其实比较好懂,就是对

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

两边点连直线求交点总数 树状数组或线段树 poj 3067 Japan

http://poj.org/problem?id=3067 Japan Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 23602   Accepted: 6369 Description Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Japan is tall island

树状数组与线段树

一:树状数组 树状数组是对一个数组改变某个元素和求和比较实用的数据结构.两中操作都是O(logn). 需求:有时候我们需要频繁地求数组的前k项和或者求数组从小标i到j的和,这样每次最坏情况下的时间复杂度就会为O(N),这样效率太低了.而树状数组主要就是为了解决这样一个问题.树状数组在求和及修改都可以在O(lgN)时间内完成. 树状数组需要额外维护一个数组,我们设为C[N],原数组为A[N], 其中每个元素C[i]表示A[i-2^k+1]到A[i]的和,这里k是i在二进制时末尾0的个数.注意通过位

hdu 3584 二进制0,1反转 三维树状数组 及三维树状数组模板

先贴自己类比二维树状数组写的三维树状数组模板: 开始的时候循环体内j=y,k=z,没写,以为自己思路错了,,,hehe..... 更高维的树状数组以此类比 const int MAXN = 100+10; int c[MAXN][MAXN][MAXN];int X,Y,Z; int N; inline int lowbit(int x){return x&(-x);} void update(int x, int y, int z, int v) { int i=x,j=y,k=z; while

HDU 1566 Color the ball(树状数组or线段树)

Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11387    Accepted Submission(s): 5680 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"

HDU 1166 敌兵布阵 (我的树状数组加线段树点修改模板)

思路:本题因为是点修改,所以我们可以用线段树或者是树状数组了.线段树的基本操作我在我的代码中会具体体现,关键是要理解下面这幅图,具体的思想大家可以去看看其他的资料 线段树AC代码: #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; #define N 50005 int num

【POJ】1990-MooFest(树状数组or线段树)

树状数组和线段树的解法比较,感觉不论在内存还是时间上都是树状数组占优 大题思路对 v从小到大进行排序,之后找之前的(也就是比v小的坐标) v * sum(abs(xi - x)) 这样的话 abs无法处理,我们用另外一个树状数组记录在x ~ y区间牛的个数,之前那个记录在x ~ y区间内牛的坐标和 Accepted 484 79 C++ 1131   树状数组代码: #include<cstdio> #include<algorithm> #include<cstring&g

洛谷P3380 【模板】二逼平衡树(树套树,树状数组,线段树)

洛谷题目传送门 emm...题目名写了个平衡树,但是这道题的理论复杂度最优解应该还是树状数组套值域线段树吧. 就像dynamic ranking那样(蒟蒻的Sol,放一个link骗访问量233) 所有的值(包括初始a数组,操作1.3.4.5的k)全部先丢进去离散化 对于1操作查比它小的数,挑log棵线段树,找区间小于这个数的个数+1,这个还比较好像 操作2就是dynamic ranking,log棵线段树一起加减,像静态主席树求第k小一样跳,操作3 dynamic ranking里也有 操作4先