BZOJ 3110 [Zjoi2013]K大数查询(整体二分+树状数组)
来源:互联网 发布:淘宝快递投诉怎么撤销 编辑:程序博客网 时间:2024/05/16 14:00
3110: [Zjoi2013]K大数查询
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 9615 Solved: 2842
[Submit][Status][Discuss]
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
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
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操作中abs(c)<=N
2操作中c<=Maxlongint
其实呢,这题和之前写过的那个动态区间第K大很类似,只不过那个是修改,而这个是插入,但是同样都可以用线段树套Treap来实现。但是今天要说的是另外一种更加巧妙的分治方法,使得编程复杂度大幅下降——整体二分。
首先,我们知道,如果仅仅只是询问区间第K大,我们用的是二分答案。具体做法是二分一个数字,然后看区间内有多少个数字比他大,如果恰好有K-1个,那么枚举到的这个数字就是区间第K大。对于这道题目,我们同样也可以运用这种思想,只不过我们还要把这个所有的操作也和这个二分答案的过程一起做,所以顾名思义这个方法叫做整体二分。我定义solve(1,m,1,n)表示解决区间[1,m]的操作,然后他们的答案在区间[1,n]中。
类似与二分答案,我要动态的统计大于mid的数字的出现次数。于是从第一个操作开始,如果是插入操作,而且插入的数字大于mid,那么把执行此操作并把这个操作丢入操作队列B中,否则不执行,操作丢入操作队列A中;如果是查询操作,那么我们就查询对应区间大于mid的数字数量,如果大于K,那么说明mid偏小了,再把它丢入操作队列B中,如果小于说明mid偏大了,K减去大于mid的数量之后再把这个操作丢入操作队列A中。执行完一遍之后,我们再来看看操作队列A和操作队列B中分别是什么。操作队列A中含有的是插入数字小于等于mid的插入操作,和假设以mid为第K大但是发现比他大的数字小于K-1的查询操作;操作队列B中是插入数字大于mid的插入操作,和假设以mid为第K大但是发现比他大的数字大于K-1的查询操作。可以看出,经过这样一个整体二分的过程,我们把所有操作分成了两个部分,而且两个部分内又是各自有着时间的顺序。我们假设操作序列A的长度为lenA,那么我们接下来要做的就是solve(1,lenA,1,mid)和solve(lenA+1,m,mid+1,n)。
精髓在于,先分再治。把操作和答案一起二分,然后二分的同时把操作和答案都分成两个部分,然后在分别治。然后在处理的过程中,我们需要一个支持区间修改,区间查询的数据结构,这里我用了树状数组,顺便补充一下模板,毕竟这个非常的短小精悍。然后又涉及到一个清零的问题,因为每次治完之后树状数组需要进行清零,这里有两种方式,一是打一个标签,实际上是时间戳,如果时间戳不同,那么在查询和修改的时候首先单个清零;另一种是在治完之后,重现便利操作,把之前更新的减去。为了方便我选择了后者,但是在常数上可能会更慢。值得注意的是,树状数组要使用LL,毕竟加的数字可能比较多。具体见代码:
#include<bits/stdc++.h>#define lowbit(x) x&-x#define LL long long#define N 100010using namespace std;struct node{ int op,l,r; LL k;int id;} p[N],tmp[2][N];int n,m,ans[N];struct EX_BIT//支持区间修改、区间查询的树状数组{ struct binaryIndexTree { LL c[N]; void init(){memset(c,0,sizeof(c));} void update(int x,LL k){for(;x<=n;c[x]+=k,x+=lowbit(x));} LL sum(int x){LL ans=0;for(;x>0;ans+=c[x],x-=lowbit(x));return ans;} } BIT1,BIT2; void init(){BIT1.init();BIT2.init();} LL getsum(int l,int r){return sum(r)-sum(l-1);} void update(int l,int r,LL x){add(l,x);add(r+1,-x);} LL sum(LL x){return (x+1)*BIT1.sum(x)-BIT2.sum(x);} void add(int x,LL k){BIT1.update(x,k);BIT2.update(x,x*k);}} BIT;void solve(int L,int R,int l,int r)//整体二分{ if (L>R) return; if (l==r)//如果到了单位答案区间,那么答案确定了 { for(int i=L;i<=R;i++) if (p[i].op==2) ans[p[i].id]=l; return; } int t1=0,t2=0,mid=(l+r)>>1; for(int i=L;i<=R;i++) { if (p[i].op==1) { if (p[i].k<=mid) tmp[0][t1++]=p[i];//对于插入操作,分为大于mid和小于等于mid两种情况 else tmp[1][t2++]=p[i],BIT.update(p[i].l,p[i].r,1); } else { LL temp=BIT.getsum(p[i].l,p[i].r); if (temp<p[i].k) p[i].k-=temp,tmp[0][t1++]=p[i];//查询操作则类似与普通二分答案,看数量是否大于K else tmp[1][t2++]=p[i]; } } int Mid=L+t1; for(int i=L;i<Mid;i++)//临时数组数据转移 { p[i]=tmp[0][i-L]; if (p[i].op==1&&p[i].k>mid) BIT.update(p[i].l,p[i].r,-1); } for(int i=Mid;i<=R;i++) { p[i]=tmp[1][i-Mid]; if (p[i].op==1&&p[i].k>mid) BIT.update(p[i].l,p[i].r,-1); } solve(L,Mid-1,l,mid); solve(Mid,R,mid+1,r);//分开成两部分去治}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int op,l,r,k; scanf("%d%d%d%d",&op,&l,&r,&k); p[i]=node{op,l,r,k,i}; } solve(1,m,1,n); for(int i=1;i<=m;i++) if (ans[i]!=0) printf("%d\n",ans[i]); return 0;}
- BZOJ 3110 [Zjoi2013]K大数查询(整体二分+树状数组)
- 【BZOJ 3110】 [Zjoi2013]K大数查询 整体二分+树状数组区间修改
- 【BZOJ】3110 [Zjoi2013]K大数查询 整体二分+树状数组 || 树套树
- bzoj 3110: [Zjoi2013]K大数查询(树套树,整体二分)
- BZOJ 3110 [Zjoi2013]K大数查询 (整体二分 + 树状数组或线段树处理区间合值)
- HDU 5412 CRB and Queries && BZOJ 3110: [Zjoi2013]K大数查询 (整体二分+树状数组/线段树)
- BZOJ-3110-K大数查询-ZJOI2013-整体二分
- [BZOJ]3110: [Zjoi2013]K大数查询 整体二分+线段树
- bzoj 3110 [Zjoi2013]K大数查询 整体二分
- 【BZOJ】【P3110】【Zjoi2013】【K大数查询】【题解】【整体二分】
- 【整体二分+树状数组区间加区间和】BZOJ3110 [Zjoi2013]K大数查询
- [Zjoi2013]K大数查询 整体二分/树套树
- [整体二分] BZOJ3110: [Zjoi2013]K大数查询
- 【BZOJ3110】K大数查询(ZJOI2013)-整体二分+线段树
- bzoj 3110 K大数查询(整体二分)
- bzoj 3110 K大数查询 整体二分
- 【ZJOI2013】K 大数查询 ( 树状数组套线段树 )
- 【bzoj 3110】K大数查询(整体二分)
- 洛谷 P2484 [SDOI2011]打地鼠
- vue.js 2.x整理
- 静态导入和自动装箱和自动拆箱
- bfs hrbust 2188
- Linux操作系统启动流程
- BZOJ 3110 [Zjoi2013]K大数查询(整体二分+树状数组)
- 使用dd测试硬盘
- 300万知乎多标签文本分类任务经验分享(附源码)
- centos php编译安装
- Spark SQL & Spark Hive编程开发, 并和Hive执行效率对比
- UI_布局之线性布局-动态生成与LayoutInflater
- jdbc报错 :com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
- Openmeetings4.0.0二次开发日志(三)使用webservice调用OM接口(1)
- 机器学习算法之卷积神经网络CNN