bzoj 3110: [Zjoi2013]K大数查询(树套树)
2017-09-05 23:05
295 查看
树套树:
本质:一棵树的每个节点套着另一棵树
通常时间复杂度:O(nlog²n)
空间复杂度:因为树的大小是nlogn,而每个节点又有一棵nlogn的树,所以最大空间复杂度为O(n²log²)
但事实上,若是动态申请,对于一般题目来讲,是不会占用这么多空间的,例如题目有m次更新,显然每次更新最多只用新建log²n个节点,空间复杂度O(mlog²n)
一般来讲第一层都是线段树
Submit: 9100 Solved: 2712
[Submit][Status][Discuss]
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
接下来M行,每行形如1 a b c或2 a b c
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
2
1
题解:
线段树套线段树
第一层是权值线段树,每个节点只存储在范围[l, r]内值的状态
第二层是区间线段树,每个节点储存在[l', r']内有多少个数
总体来讲就是每个节点存储区间[l', r']内有多少个范围在[l, r]内的数
题目的数据范围是-n<=x<=n,理论上要离散化的,并且加强过数据,可是没离散化还是过了。。。
注意要用无符号整型,而且lazy标记最好不要更新
除此之外,第k大即第n-k+1小
#include<stdio.h>
#include<algorithm>
using namespace std;
#define uint unsigned int
uint n, cnt, root[200005], tre[6400005], lt[6400005], rt[6400005], lazy[6400005];
void Update2(uint &now, uint l, uint r, uint L, uint R)
{
uint m;
if(now==0)
now = ++cnt;
if(l>=L && r<=R)
{
lazy[now]++;
tre[now] += r-l+1;
return;
}
m = (l+r)/2;
if(L<=m)
Update2(lt[now], l, m, L, R);
if(R>=m+1)
Update2(rt[now], m+1, r, L, R);
tre[now] = tre[lt[now]]+tre[rt[now]]+lazy[now]*(r-l+1);
}
void Update1(uint l, uint r, uint x, uint L, uint R, uint k)
{
uint m;
Update2(root[x], 1, n, L, R);
if(l==r)
return;
m = (l+r)/2;
if(k<=m)
Update1(l, m, x*2, L, R, k);
else
Update1(m+1, r, x*2+1, L, R, k);
}
uint Query2(uint now, uint l, uint r, uint L, uint R)
{
uint m, sum;
sum = 0;
if(now==0)
return 0;
if(l>=L && r<=R)
return tre[now];
m = (l+r)/2;
if(L<=m)
sum += Query2(lt[now], l, m, L, R);
if(R>=m+1)
sum += Query2(rt[now], m+1, r, L, R);
return sum+lazy[now]*(min(R, r)-max(L, l)+1);
}
uint Query1(uint l, uint r, uint x, uint L, uint R, uint k)
{
uint m, sum;
if(l==r)
return l;
m = (l+r)/2;
sum = Query2(root[x*2], 1, n, L, R);
if(sum>=k)
return Query1(l, m, x*2, L, R, k);
else
return Query1(m+1, r, x*2+1, L, R, k-sum);
}
int main(void)
{
uint m, opt, L, R, k;
scanf("%d%d", &n, &m);
n += 1;
while(m--)
{
scanf("%d%d%d%d", &opt, &L, &R, &k);
if(opt==1)
{
k = n-k;
Update1(1, n, 1, L, R, k);
}
else
printf("%d\n", n-Query1(1, n, 1, L, R, k));
}
return 0;
}
本质:一棵树的每个节点套着另一棵树
通常时间复杂度:O(nlog²n)
空间复杂度:因为树的大小是nlogn,而每个节点又有一棵nlogn的树,所以最大空间复杂度为O(n²log²)
但事实上,若是动态申请,对于一般题目来讲,是不会占用这么多空间的,例如题目有m次更新,显然每次更新最多只用新建log²n个节点,空间复杂度O(mlog²n)
一般来讲第一层都是线段树
3110: [Zjoi2013]K大数查询
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 9100 Solved: 2712
[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
2 51 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
12
1
题解:
线段树套线段树
第一层是权值线段树,每个节点只存储在范围[l, r]内值的状态
第二层是区间线段树,每个节点储存在[l', r']内有多少个数
总体来讲就是每个节点存储区间[l', r']内有多少个范围在[l, r]内的数
题目的数据范围是-n<=x<=n,理论上要离散化的,并且加强过数据,可是没离散化还是过了。。。
注意要用无符号整型,而且lazy标记最好不要更新
除此之外,第k大即第n-k+1小
#include<stdio.h>
#include<algorithm>
using namespace std;
#define uint unsigned int
uint n, cnt, root[200005], tre[6400005], lt[6400005], rt[6400005], lazy[6400005];
void Update2(uint &now, uint l, uint r, uint L, uint R)
{
uint m;
if(now==0)
now = ++cnt;
if(l>=L && r<=R)
{
lazy[now]++;
tre[now] += r-l+1;
return;
}
m = (l+r)/2;
if(L<=m)
Update2(lt[now], l, m, L, R);
if(R>=m+1)
Update2(rt[now], m+1, r, L, R);
tre[now] = tre[lt[now]]+tre[rt[now]]+lazy[now]*(r-l+1);
}
void Update1(uint l, uint r, uint x, uint L, uint R, uint k)
{
uint m;
Update2(root[x], 1, n, L, R);
if(l==r)
return;
m = (l+r)/2;
if(k<=m)
Update1(l, m, x*2, L, R, k);
else
Update1(m+1, r, x*2+1, L, R, k);
}
uint Query2(uint now, uint l, uint r, uint L, uint R)
{
uint m, sum;
sum = 0;
if(now==0)
return 0;
if(l>=L && r<=R)
return tre[now];
m = (l+r)/2;
if(L<=m)
sum += Query2(lt[now], l, m, L, R);
if(R>=m+1)
sum += Query2(rt[now], m+1, r, L, R);
return sum+lazy[now]*(min(R, r)-max(L, l)+1);
}
uint Query1(uint l, uint r, uint x, uint L, uint R, uint k)
{
uint m, sum;
if(l==r)
return l;
m = (l+r)/2;
sum = Query2(root[x*2], 1, n, L, R);
if(sum>=k)
return Query1(l, m, x*2, L, R, k);
else
return Query1(m+1, r, x*2+1, L, R, k-sum);
}
int main(void)
{
uint m, opt, L, R, k;
scanf("%d%d", &n, &m);
n += 1;
while(m--)
{
scanf("%d%d%d%d", &opt, &L, &R, &k);
if(opt==1)
{
k = n-k;
Update1(1, n, 1, L, R, k);
}
else
printf("%d\n", n-Query1(1, n, 1, L, R, k));
}
return 0;
}
相关文章推荐
- BZOJ 3110 ZJOI 2013 K大值查询 线段树套线段树
- BZOJ 3110 [Zjoi2013]K大数查询 ——树套树
- 树套树专题——bzoj 3110: [Zjoi2013] K大数查询 & 3236 [Ahoi2013] 作业 题解
- Bzoj3110: [Zjoi2013]K大数查询
- 【ZJOI2013】bzoj3110 K大数查询【解法二】
- [BZOJ3110][ZJOI2013]K大数查询 树套树/CDQ分治
- 【整体二分】[ZJOI 2013] bzoj3110 K大数查询
- BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)
- 【bzoj 3110】[Zjoi2013]K大数查询
- 【BZOJ3110】K大数查询(ZJOI2013)-整体二分+线段树
- BZOJ3110 ZJOI2013 K大数查询
- BZOJ.3110.[ZJOI2013]K大数查询(整体二分 树状数组/线段树)
- Bzoj3110: [Zjoi2013]K大数查询
- [BZOJ3110][Zjoi2013]-K大数查询-树套树
- [BZOJ 3110] [Zjoi2013] K大数查询 【树套树】
- 【ZJOI2013】bzoj3110 K大数查询【解法一】
- 【ZJOI2013】【BZOJ3110】K大数查询
- [BZOJ3110][Zjoi2013]K大数查询
- BZOJ_3110_[Zjoi2013]K大数查询_整体二分+树状数组
- BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )