BZOJ Zjoi2013 K大数查询

  刚学了整体二分,跟随神犇的步伐走向了这道题......

  神犇:这道题不是二分答案裸题吗? 我:......

  也许是我真的太弱了吧:

  不过好歹是A了,讲一讲我的思路:

  首先,我们二分出一个答案mid,然后扫一遍当前区间内的询问,如果加入的数x>=mid,那么把这段区间的值都加1;这样就可以求出区间>=mid的数的个数了。

  然后,根据这些东西判断一下当前询问该丢到左边还是右边,递归处理就可以了。还有不要忘了询问的是区间第k大,所以对于丢到左边的询问要先把贡献给算进去。

  下面贴代码:

 1 #include<cstdio>
 2 #define maxn 50010
 3
 4 using namespace std;
 5 typedef long long llg;
 6
 7 struct data{
 8     int tp,l,r,k,id;
 9 }s[maxn],zl[maxn],zr[maxn];
10 int n,m,ans[maxn],tt;
11 llg c1[maxn],c2[maxn];
12
13 int getint(){
14     int w=0,q=0;
15     char c=getchar();
16     while((c<‘0‘||c>‘9‘)&&c!=‘-‘) c=getchar();
17     if(c==‘-‘) q=1,c=getchar();
18     while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar();
19     return q?-w:w;
20 }
21
22 void add(int x,int y){for(int i=x;i<=n;i+=i&(-i)) c1[i]+=y,c2[i]+=(llg)x*y;}
23 llg sum(int x){
24     llg ans(0);
25     for(int i=x;i;i-=i&(-i)) ans+=(x+1)*c1[i]-c2[i];
26     return ans;
27 }
28
29 void solve(int top,int end,int l,int r){
30     if(l==r){
31         for(int i=top;i<=end;i++)
32             ans[s[i].id]=l;
33         return;
34     }
35     int mid=l+r+1>>1,lo(0),ro(0);
36     bool ll(0),rr(0);llg x;
37     for(int i=top;i<=end;i++)
38         if(s[i].tp==1)
39             if(s[i].k>=mid) add(s[i].l,1),add(s[i].r+1,-1),zr[++ro]=s[i];
40             else zl[++lo]=s[i];
41         else{
42             x=sum(s[i].r)-sum(s[i].l-1);
43             if(x>=s[i].k) zr[++ro]=s[i],rr=1;
44             else s[i].k-=x,zl[++lo]=s[i],ll=1;
45         }
46     for(int i=top;i<=end;i++)
47         if(s[i].tp==1 && s[i].k>=mid) add(s[i].l,-1),add(s[i].r+1,1);
48     for(int i=1;i<=lo;i++) s[top+i-1]=zl[i];
49     for(int i=1;i<=ro;i++) s[top+i+lo-1]=zr[i];
50     if(ll) solve(top,top+lo-1,l,mid-1);
51     if(rr) solve(top+lo,end,mid,r);
52 }
53
54 int main(){
55     n=getint();m=getint();
56     for(int i=1;i<=m;i++){
57         s[i].tp=getint();
58         s[i].l=getint(); s[i].r=getint();
59         s[i].k=getint();
60         if(s[i].tp==2) s[i].id=++tt;
61     }
62     solve(1,m,1,n);
63     for(int i=1;i<=tt;i++)
64         printf("%d\n",ans[i]);
65     return 0;
66 }
时间: 2025-01-19 21:21:17

BZOJ Zjoi2013 K大数查询的相关文章

树套树专题——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大数查询( 树状数组套主席树 )

BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ----------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std;

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大数查询 树套树

[BZOJ3110][Zjoi2013]K大数查询 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

洛谷 P3332 [ZJOI2013]K大数查询 || bzoj3110

用树套树就很麻烦,用整体二分就成了裸题.... 错误: 1.尝试线段树套平衡树,码农,而且n*log^3(n)慢慢卡反正我觉得卡不过去 2.线段树pushdown写错...加法tag对于区间和的更新应该要乘上区间长度的 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 typedef long long LL; 6 struct Q 7 { 8 LL

[ZJOI2013]K大数查询——整体二分

新科技:整体二分 它能解决的典型问题:带修改区间第\(k\)大 大概的做法是这样的:我们一次二分一个值\(mid\),然后依据操作的答案与\(mid\)的大小关系把操作分别划到两边,然后递归下去.也就是相当于二分的是所有询问的答案 感觉其实这个跟在权值线段树上二分一个效果,只是用离线的方式替代掉了那一层权值线段树而已 计算可得复杂度为\(O(nlog^2n)\)(由主定理,\(T(n)=2T(n/2)+O(nlogn)=O(nlog^2n)\)) 拿线段树或者树状数组维护都行 板子题是这一道K大

BZOJ 3110 K大数查询 树套树

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

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

题目链接: BZOJ - 3110 题目分析 这道题是一道树套树的典型题目,我们使用线段树套线段树,一层是区间线段树,一层是权值线段树.一般的思路是外层用区间线段树,内层用权值线段树,但是这样貌似会很难写.多数题解都使用了外层权值线段树,内层区间线段树,于是我就这样写了.每次插入会在 logn 棵线段树中一共建 log^2(n) 个结点,所以空间应该开到 O(nlog^2(n)) .由于这道题查询的是区间第 k 大,所以我们存在线段树中的数值是输入数值的相反数(再加上 n 使其为正数),这样查第