数据结构(树套树):ZJOI 2013 K大数查询

  

  有几个点卡常数……

  发现若第一维为位置,第二维为大小,那么修改时第一维修改区间,查询时第一维查询区间,必须挂标记。而这种情况下标记很抽象,而且Push_down不是O(1)的,并不可行。
  那要怎么做呢?不妨交换一下,第一维为大小,第二维为位置,在第二维中挂标记,这样Push_down就是O(1)的了。
  做完这道题,我最大的启发就是:树套树不适于在第一维挂标记,因为标记的维度会是一维的,根本不好维护。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <ctime>
 5 using namespace std;
 6 const int maxn=50010;
 7 const int maxm=20000010;
 8 int rt[maxn*4],tot,n,m;
 9 int ch[maxm][2],sum[maxm],tag[maxm];
10
11 void Push_up(int x){
12     sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
13 }
14
15 void Add(int &x,int l,int r,int d){
16     if(!x)x=++tot;
17     sum[x]+=(r-l+1)*d;
18     tag[x]+=d;
19 }
20
21 void Push_down(int x,int l,int r){
22     if(tag[x]&&l!=r){
23         int mid=(l+r)>>1;
24         if(!ch[x][0])ch[x][0]=++tot;
25         if(!ch[x][1])ch[x][1]=++tot;
26         sum[ch[x][0]]+=(mid-l+1)*tag[x];tag[ch[x][0]]+=tag[x];
27         sum[ch[x][1]]+=(r-mid)*tag[x];tag[ch[x][1]]+=tag[x];
28         tag[x]=0;
29     }
30 }
31
32 void Update(int &x,int l,int r,int a,int b){
33     if(!x)x=++tot;
34     Push_down(x,l,r);
35     if(l>=a&&r<=b){sum[x]+=r-l+1;tag[x]+=1;return;}
36     int mid=(l+r)>>1;
37     if(mid>=a)Update(ch[x][0],l,mid,a,b);
38     if(mid<b)Update(ch[x][1],mid+1,r,a,b);
39     Push_up(x);
40 }
41
42 void Modify(int x,int l,int r,int g,int a,int b){
43     Update(rt[x],1,n,a,b);
44     if(l==r)return;
45     int mid=(l+r)>>1;
46     if(mid>=g)Modify(x<<1,l,mid,g,a,b);
47     else Modify(x<<1|1,mid+1,r,g,a,b);
48 }
49
50 int Query(int x,int l,int r,int a,int b){
51     if(!x)return 0;
52     Push_down(x,l,r);
53     if(l>=a&&r<=b)return sum[x];
54     int mid=(l+r)>>1,ret=0;
55     if(mid>=a)ret=Query(ch[x][0],l,mid,a,b);
56     if(mid<b)ret+=Query(ch[x][1],mid+1,r,a,b);
57     return ret;
58 }
59
60 int Solve(int l,int r,int k){
61     int lo=1,hi=n,p=1;
62     while(lo<hi){
63         int mid=(lo+hi)>>1,tmp;
64         tmp=Query(rt[p<<1],1,n,l,r);
65         if(tmp>=k)hi=mid,p<<=1;
66         else lo=mid+1,p=p<<1|1,k-=tmp;
67     }
68     return hi;
69 }
70
71 int op,a,b,c;
72 int main(){
73 #ifndef ONLINE_JUDGE
74     freopen("zjoi13_sequence.in","r",stdin);
75     freopen("zjoi13_sequence.out","w",stdout);
76 #endif
77     scanf("%d%d",&n,&m);
78     while(m--){
79         scanf("%d%d%d%d",&op,&a,&b,&c);
80         if(op==1)Modify(1,1,n,n-c+1,a,b);
81         else printf("%d\n",n-Solve(a,b,c)+1);
82     }
83     //printf("%.2f\n",(double)clock()/CLOCKS_PER_SEC);
84     return 0;
85 }
时间: 2025-01-15 13:15:26

数据结构(树套树):ZJOI 2013 K大数查询的相关文章

BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

题目大意:有一些位置,这些位置上可以放若干个数字.现在有两种操作. 1.在区间l到r上添加一个数字x 2.求出l到r上的第k大的数字是什么 思路:这种题一看就是树套树,关键是怎么套,怎么写.(话说我也不会来着..)最容易想到的方法就是区间线段树套一个权值线段树,但是区间线段树上的标记就会变得异常复杂.所以我们就反过来套,用权值线段树套区间线段树.这样修改操作在外线段树上就变成了单点修改,外线段树就不用维护标记了.在里面的区间线段树上维护标记就容易多了.具体实现见代码. CODE: #includ

[bzoj 3110][zjoi 2013]K大数查询

传送门 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Solution 这题是可以区间线段树套权值线段树来做的 但是我想练一下整体二分 顺便写一个树状数组的区间修改+区间查询 class BIT { #define NM 50005 #define lb(x) (x&(-x)) private: ll t1[NM],t2

[ZJOI 2013] K大数查询

[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=3110 [算法] 整体二分 + 线段树 时间复杂度 : O(NlogN ^ 2) [代码] #include<bits/stdc++.h> using namespace std; #define MAXN 500010 typedef long long ll; typedef long double ld; struct query { int type , a , b; ll

树套树专题——bzoj 3110: [Zjoi2013] K大数查询 &amp; 3236 [Ahoi2013] 作业 题解

[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 978  Solved: 476 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 Outpu

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[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

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] 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

【BZOJ3110】【Zjoi2013】K大数查询 树套树 权值线段树套区间线段树

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43020009"); } 题解: 外层权值线段树,内层区间线段树可解. 权值都是1~n,就不用离散化了. 我写了标记永久化. 其它心得神马的: 天生对树形数据结构无爱. 第一次写树套树,终于知道是怎么回事了. (只针对本题) 就是外层每个点都表示了一段

BZOJ 3110 K大数查询 树套树

题意:链接 方法:树套树(线段树套线段树) 题解:这题好神啊- -自己在做的时候一顿yy也没yy出用两个线段树来搞,其实我想的是类似二逼那道题那样,用线段树维护总区间,treap维护每个节点,不过这样的话,尼玛修改就是暴力有没有?而且查询的时候也是暴力啊有没有?绝壁不是这么做的啊! 上网上找了找题解综合了大家的思想自己也是懂了这题是咋回事了,也是跪了. 好不扯淡了,谈正经的,两个线段树是怎么搞得. 其实第一棵线段树是区间的线段树,而第二棵是维护值域的线段树,具体解析请看代码. #include

[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 大数是多少. Sam