【BZOJ3110】【ZJOI2013】K大数查询
2016-02-10 21:42
435 查看
3110: [Zjoi2013]K大数查询
Time Limit: 20 Sec Memory Limit: 512 MB
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操作中abs(c)<=N
2操作中abs(c)<=Maxlongint
Source
啊这道题解法貌似不少据说有三种,这里详细下线段树套线段树。
第一层线段树是权值线段树,它的每个节点对应了整个区间的线段树,并且第二层的线段树维护了第一层权值区间中所有数出现的总次数。
插入一种颜色c的时候,在第一层相当于单点修改,会递归地影响线段树上O(logn)个结点,对于影响到的每个节点,都只需要再在第二层线段树上做一次区间修改即可(之所以要将权值放在第一层,就是因为要在第二层线段树上的节点打lazy标记,而我实在是想不出来第一层的线段树该如何处理lazy的下放问题……)。为了减少常数,不妨在实现时使lazy标记永久化。
查询时,用类似于二分查找的办法(这个貌似跟主席树是类似的),假设当前已经确定了答案的区间为[l,r],设其中点为m,第二层线段树查询的区间为[ql,qr],由于是查询第k大的数,我们便查询在[ql,qr]中,所有在[m+1,r]范围内的颜色出现的总次数t,若t>=k,则说明答案在右区间;反之,说明答案在左区间,同时将k减去t。
代码真是比想象中的短多了……
BTW,本题良心数据没有负数。
Time Limit: 20 Sec Memory Limit: 512 MB
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操作中abs(c)<=N
2操作中abs(c)<=Maxlongint
Source
啊这道题解法貌似不少据说有三种,这里详细下线段树套线段树。
第一层线段树是权值线段树,它的每个节点对应了整个区间的线段树,并且第二层的线段树维护了第一层权值区间中所有数出现的总次数。
插入一种颜色c的时候,在第一层相当于单点修改,会递归地影响线段树上O(logn)个结点,对于影响到的每个节点,都只需要再在第二层线段树上做一次区间修改即可(之所以要将权值放在第一层,就是因为要在第二层线段树上的节点打lazy标记,而我实在是想不出来第一层的线段树该如何处理lazy的下放问题……)。为了减少常数,不妨在实现时使lazy标记永久化。
查询时,用类似于二分查找的办法(这个貌似跟主席树是类似的),假设当前已经确定了答案的区间为[l,r],设其中点为m,第二层线段树查询的区间为[ql,qr],由于是查询第k大的数,我们便查询在[ql,qr]中,所有在[m+1,r]范围内的颜色出现的总次数t,若t>=k,则说明答案在右区间;反之,说明答案在左区间,同时将k减去t。
代码真是比想象中的短多了……
BTW,本题良心数据没有负数。
#include<iostream> #include<cstdio> #define maxn 50000 #define maxm 20000000 #define mid (l+r>>1) using namespace std; int lc[maxm+10],rc[maxm+10],sum[maxm+10],val[maxm+10],root[(maxn<<4)+10],ql,qr,cnt; void update(int &o,int l,int r){ if(!o)o=++cnt; if(ql<=l&&r<=qr)val[o]++,sum[o]+=r-l+1; else{ int m=mid; if(ql<=m)update(lc[o],l,m); if(qr>m)update(rc[o],m+1,r); sum[o]=sum[lc[o]]+sum[rc[o]]+val[o]*(r-l+1); } } void query(int &o,int l,int r,int &qsum,int add){ if(!o)o=++cnt; if(ql<=l&&r<=qr)qsum+=sum[o]+add*(r-l+1); else{ int m=mid; if(ql<=m)query(lc[o],l,m,qsum,add+val[o]); if(qr>m)query(rc[o],m+1,r,qsum,add+val[o]); } } int main(){ int n,q; scanf("%d%d",&n,&q); int k,a,b,c; while(q--){ scanf("%d%d%d%d",&k,&ql,&qr,&c); int o=1,l=1,r=n; if(k==1){ for(;;){ update(root[o],1,n); if(l==r)break; int m=mid; if(c<=m)o=o<<1,r=m; else o=o<<1|1,l=m+1; } }else{ int t; for(;;){ if(l==r)break; int m=mid; query(root[o<<1|1],1,n,t=0,0); if(t>=c){ o=o<<1|1; l=m+1; }else{ c-=t; o=o<<1; r=m; } } printf("%d\n",l); } } return 0; }
相关文章推荐
- 线段树题集
- 线段树
- hdu1754
- HDU1394
- 敌兵布阵 (1)
- I Hate It (1)
- LCIS (2)
- A Simple Problem with Integers (2)
- Mayor's posters (3)
- Buy Tickets (3)
- 线段树
- UVA - 12532 Interval Product
- POJ 3264 Balanced Lineup
- hdu 1542 求矩形并的面积
- 关于数据结构之线段树
- poj 3225 关于集合运算
- poj 2352
- hihocoder #1069 线段树
- hdu1166敌兵布阵(线段树点修改)
- 【51nod 6级题目】XOR key 问题