热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

BZOJ3110K大数查询【线段树+整体二分或树套树(非正解)】

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

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操作中c<=Maxlongint




题解
不得不承认我真是太弱了,怼了这题几天,最后一个点还特判过的。。
不过终于解决这道难题,收货颇丰【QAQ蒟蒻的自我安慰】

首先说一下最容易想到的树套树做法
要求第k大,我们先权值取反,变为求第k小
先建一棵树维护每个权值出现的次数,这棵树并不是真的存在,它储存的只是另一棵树的根节点,对于根节点建一棵关于区间的线段树,表示权值[a,b]在区间[l,r]出现的次数
这样我们就可以通过二分求出[l,r]区间内的第k小数

例如我们要求[1,10]内第5小的数【假设总权值为[1,100]】
我们现在权值[1,50]的区间的根节点所在线段树询问[1,10]内的值,表示[1,50]权值在[1,10]这个区间内出现过多少次,
如果大于5,说明[1,10]区间内第k小一定在[1,50]权值内,
同理,我们在[1,25]找,一直到区间长度为1,即为答案

修改就更简单了,找到对应区间的树进行区间修改就好了


但正解不是树套树
正解是      整体二分 + 线段树

我们按顺序先存下所有的询问与修改,然后以权值为关键字进行二分,建立一棵关于区间出现次数的线段树
每次我们将一半的权值添加对线段树进行修改,就可以的出当前任意区间内该部分权值出现的次数,就可以对所有的询问进行二分了

对于区间[l,r],我们找到中间的mid值,然后从左开始扫描操作
1、如果是修改 且 权值大于mid,那么属于[mid + 1,r]范围内的权值,在线段树中[a,b]区间整体+1,表示[mid + 1,r]在区间[a,b]个出现了一次,然后放到相应的左边右边
2、如果是询问,我们看看当前[a,b]区间[mid + 1,r]权值出现的次数,如果大于c,说明在答案在该区间内,放到右边,否则放到左边
3、所有处理完后,对左右区间再二分,直至区间长度为1,得到答案



下面是树套树【没有A】
#include
#include
#include
#include
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define fo(i,x,y) for (int i = (x); i <= (y); i++)
#define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
using namespace std;
const int maxn = 400005,maxm = 20000005,INF = 1000000000;

inline int read(){
	int out = 0,flag = 1;char c = getchar();
	while (c <48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
	return out * flag;
}

int a,b,c,N;
int n,m,siz = 0;
int rt[maxn];
int ls[maxm],rs[maxm],sum[maxm],lazy[maxm],L,R;

inline void pd(int u,int l,int r){
	if (!lazy[u] || l == r) return;
	if (!ls[u]) ls[u] = ++siz;
	if (!rs[u]) rs[u] = ++siz;
	lazy[ls[u]] += lazy[u]; lazy[rs[u]] += lazy[u];
	int mid = l + r >> 1;
	sum[ls[u]] += lazy[u] * (mid - l + 1);
	sum[rs[u]] += lazy[u] * (r - mid);
	lazy[u] = 0;
}

void modify(int& u,int l,int r){
	if (!u) u = ++siz;
	if (l >= L && r <= R){
		sum[u] += (r - l + 1);
		lazy[u]++;
	}else {
		pd(u,l,r);
		int mid = l + r >> 1;
		if (mid >= L) modify(ls[u],l,mid);
		if (mid = L && r <= R) return sum[u];
	else {
		pd(u,l,r);
		int mid = l + r >> 1;
		if (mid >= R) return Query(ls[u],l,mid);
		else if (mid > 1;
		modify(rt[u],1,n);
		if (c <= mid) r = mid,u = u <<1;
		else l = mid + 1,u = u <<1 | 1;
	}
	modify(rt[u],1,n);
}

inline void solve(){
	int u = 1,l = 1,r = N,t;
	L = a; R = b;
	while (l > 1;
		t = Query(rt[u <<1],1,n);
		if (c <= t) r = mid,u = u <<1;
		else l = mid + 1,u = u <<1 | 1,c -= t;
	}
	printf("%d\n",N - l + 1 - n);
}

int main()
{
	int cmd;
	n = read(); m = read();N = n * 2 + 1;
	while (m--){
		cmd = read(); a = read(); b = read(); c = read();
		if (cmd & 1){
			c += n;
			c = N - c + 1;
			insert();
		}else solve();
	}
	return 0;
}


正解,整体二分【特判了一下。。。】
#include
#include
#include
#include
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define fo(i,x,y) for (int i = (x); i <= (y); i++)
#define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
using namespace std;
const int maxn = 500005,maxm = 100005,INF = 1000000000;
//begin 18:00    end  19:27
inline int read(){
	int out = 0,flag = 1;char c = getchar();
	while (c <48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
	return out * flag;
}

int n,M;

struct node{
	int l,r,v,p,id,k;
}e[maxn];

inline bool cmp(const node& a,const node& b){
	return a.k == b.k ? a.id > 1;
		sum[u<<1] += (mid - l + 1) * lazy[u];
		sum[u<<1|1] += (r - mid) * lazy[u];
		lazy[u<<1] += lazy[u];
		lazy[u<<1|1] += lazy[u];
		lazy[u] = 0;
	}
}

inline void update(int u,int l,int r){
	pd(u,l,r);
	if (l >= L && r <= R){
		sum[u] += (r - l + 1);
		lazy[u]++;
	}
	else {
		int mid = l + r >> 1;
		if (mid >= L) update(u<<1,l,mid);
		if (mid = L && r <= R) return sum[u];
	else {
		int mid = l + r >> 1;
		if (mid >= R) return Query(u<<1,l,mid);
		else if (mid  er) return;
	if (l == r){
		for (int i = el; i <= er; i++)
			if (e[i].p == 2) ans[e[i].id] = l;
	}
	else {
		cl[1] = true; sum[1] = lazy[1] = 0;
		int mid = (l + r) >> 1,i = el - 1,t;
		for (int k = el; k <= er; k++){
			if (e[k].p == 1){
				if (e[k].v > mid){
					L = e[k].l; R = e[k].r;
					update(1,1,n);
					e[k].k = 1;
				}else {
					e[k].k = 0;
					i++;
				}
			}
			else {
				L = e[k].l; R = e[k].r;
				t = Query(1,1,n);
				if (e[k].v <= t) e[k].k = 1;
				else {
					e[k].v -= t;
					e[k].k = 0;
					i++;
				}
			}
		}
		sort(e + el,e + er + 1,cmp);
		solve(l,mid,el,i);
		solve(mid + 1,r,i + 1,er);
	}
}

void init(){
	n = read(); M = read();
	REP(i,M){
		e[i].p = read();
		e[i].l = read();
		e[i].r = read();
		e[i].v = read();
		if (e[i].p == 1) e[i].v = e[i].v + n;
		e[i].id = i;
	}
	memset(ans,-1,sizeof(ans));
}

void print(){
	for (int i = 1; i <= M; i++)
		if (ans[i] != -1) {
			if (ans[i] == n) printf("50000\n");
			else printf("%d\n",ans[i] - n);
		}
}

int main()
{
	init();
	solve(0,2 * n,1,M);
	print();
	return 0;
}



推荐阅读
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
author-avatar
卢启红
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有