【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

题目描述

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c。如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

输入

第一行N,M
接下来M行,每行形如1 a b c或2 a b c

输出

输出每个询问的结果

样例输入

2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3

样例输出

1
2
1



题解

整体二分+树状数组区间修改

当年naive的树套树题解

前两天由于要讲整体二分,所以自己YY出了这种带修改的整体二分写法:

由于每次插入的权值时固定的,因此可以把修改和询问放到一起整体二分。

设 $solve(b,e,l,r)$ 表示解决 $[b,e]$ 中的修改和询问,其中:修改的权值、询问的答案都在 $[l,r]$ 内。

那么如果 $l=r$ ,则区间内所有询问的答案都等于 $l$ 。

否则设 $mid=\frac{l+r}2$ ,然后按照时间序扫一遍修改和询问。

对于修改:如果权值在 $[l,mid]$ 则丢到左区间,否则给这段区间整体+1并丢到右区间。

对于询问:如果区间和(实际含义为该次询问时区间内权值在 $[mid+1,r]$ 内的数的个数)大于等于询问的 $c$ ,说明 $[mid+1,r]$ 内有足够多的数,答案在 $[mid+1,r]$ 内,丢到右区间;否则把 $c$ 减去这个区间和,并丢到左区间。

清空数据结构,递归左右区间。注意对于左右区间也需要维护时间序。

需要实现:区间修改、区间求和,可以使用树状数组区间修改实现。具体可以参考 【bzoj3132】上帝造题的七分钟

时间复杂度 $O(n\log^2n)$ ,比树套树快了10倍多。。。(当然也有可能是我当年写的不好吧)

注意本题爆int,因此需要unsigned int(比long long略快)。

#include <cstdio>
#define N 50010
typedef unsigned int uint;
struct data
{
	int opt , a , b , c , id;
}q[N] , t[N];
int flag[N] , ans[N] , n;
struct bit
{
	uint v[N];
	inline void add(int x , int a)
	{
		int i;
		for(i = x ; i <= n ; i += i & -i) v[i] += a;
	}
	inline uint query(int x)
	{
		int i;
		uint ans = 0;
		for(i = x ; i ; i -= i & -i) ans += v[i];
		return ans;
	}
}A , B;
inline void modify(int x , int a)
{
	A.add(x , a) , B.add(x , a * x);
}
inline uint ask(int x)
{
	return (x + 1) * A.query(x) - B.query(x);
}
void solve(int b , int e , int l , int r)
{
	if(l == r)
	{
		int i;
		for(i = b ; i <= e ; i ++ )
			if(q[i].opt == 2)
				ans[q[i].id] = l;
		return;
	}
	int mid = (l + r) >> 1 , i , lp = b - 1 , rp = e + 1;
	uint v;
	for(i = b ; i <= e ; i ++ )
	{
		if(q[i].opt == 1)
		{
			if(q[i].c > mid) modify(q[i].a , 1) , modify(q[i].b + 1 , -1) , t[--rp] = q[i];
			else t[++lp] = q[i];
		}
		else
		{
			v = ask(q[i].b) - ask(q[i].a - 1);
			if((uint)q[i].c <= v) t[--rp] = q[i];
			else q[i].c -= v , t[++lp] = q[i];
		}
	}
	for(i = b ; i <= e ; i ++ )
		if(q[i].opt == 1 && q[i].c > mid)
			modify(q[i].a , -1) , modify(q[i].b + 1 , 1);
	for(i = b ; i <= lp ; i ++ ) q[i] = t[i];
	for(i = rp ; i <= e ; i ++ ) q[i] = t[e + rp - i];
	solve(b , lp , l , mid) , solve(rp , e , mid + 1 , r);
}
int main()
{
	int m , i;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%d%d%d%d" , &q[i].opt , &q[i].a , &q[i].b , &q[i].c) , q[i].id = i;
		if(q[i].opt == 2) flag[i] = 1;
	}
	solve(1 , m , -n , n);
	for(i = 1 ; i <= m ; i ++ )
		if(flag[i])
			printf("%d\n" , ans[i]);
	return 0;
}
时间: 2024-10-09 07:57:39

【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改的相关文章

【BZOJ3110】【整体二分+树状数组区间修改/线段树】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 Sample Output 1 2 1 HINT

BZOJ 3110 [Zjoi2013]K大数查询 ——整体二分

[题目分析] 整体二分显而易见. 自己YY了一下用树状数组区间修改,区间查询的操作. 又因为一个字母调了一下午. 貌似树状数组并不需要清空,可以用一个指针来维护,可以少一个log 懒得写了. [代码] #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 50005 #define inf

【BZOJ-3110】K大数查询 整体二分 + 线段树

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

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

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

BZOJ 3110: [Zjoi2013]K大数查询 [整体二分]

有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. N,M<=50000,N,M<=50000a<=b<=N1操作中abs(c)<=N2操作中c<=Maxlongint 之前用树套树抄过一次...然而我并不适合写那玩意儿... 加上时间序的整体二分 普通的整体二分先处理了所有$[l,mid]$的影响因子在计算询问的答案来分组

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

终于入门整体二分了,勉勉强强算是搞懂了一个题目吧. 整体二分很多时候可以比较好的离线处理区间\(K\)大值的相关问题.考虑算法流程: 操作队列\(arr\),其中有询问和修改两类操作. 每次在答案的可行值域上二分一个\(mid\),把询问的答案\(>mid\)的分在\(R\)部,\(<=mid\)的分在\(L\)部.把修改的值\(>mid\)的分在\(R\)部,\(<=mid\)的分在\(L\)部. 何谓整体二分?就是直接一起二分所有的询问操作的答案,然后暴力扫描当前操作区间,将其

【bzoj2527】[Poi2011]Meteors 整体二分+树状数组

题目描述 有N个成员国.现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站. 这个星球经常会下陨石雨.BIU已经预测了接下来K场陨石雨的情况.BIU的第i个成员国希望能够收集Pi单位的陨石样本.你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石. 输入 第一行是两个数N,M. 第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站. 第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量. 第四行有一个

【BZOJ-2527】Meteors 整体二分 + 树状数组

2527: [Poi2011]Meteors Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 831  Solved: 306[Submit][Status][Discuss] Description Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The planet is unsuitable for colo

【bzoj5173】[Jsoi2014]矩形并 扫描线+二维树状数组区间修改区间查询

题目描述 JYY有N个平面坐标系中的矩形.每一个矩形的底边都平行于X轴,侧边平行于Y轴.第i个矩形的左下角坐标为(Xi,Yi),底边长为Ai,侧边长为Bi.现在JYY打算从这N个矩形中,随机选出两个不同的矩形,并计算它们的并的大小.JYY想知道,交的大小的期望是多少.换句话说即求在所有可能的选择中,两个矩形交的面积的平均大小是多大. 输入 输入一行包含一个正整数N. 接下来N行,每行4个整数,分别为Xi,Yi,Ai,Bi 2 < =  N < =  2*10^5, 0 < =  Xi,