首页 > 其他分享 >整体二分 第k大数字

整体二分 第k大数字

时间:2024-01-15 16:49:20浏览次数:22  
标签:二分 le 数字 int 分治 整体 tot ++

整体二分,在蓝书上被称为基于值域的整体分治算法
我更喜欢叫他整体二分(因为我感觉这样叫更牛逼一点)

发现了分治有一个必须满足的性质
如果我要把一个区间问题分治,那么这个被分治的区间的前后应该要么是单方面贡献,要么是结果根本不相关,也就是前面的对后面的没有影响

其实这个总结非常非常不严谨,估计除了我自己没有人能够理解我到底在说什么
因为我说的这两个分治根本就是不同的问题,前面的是CDQ分治,这个是明显是包含修改的。只不过,很多的时候修改被理解为带时间戳的数据而已,CDQ分治根本上能解决的还是简单的多维偏序。反正我目前看到的CDQ分治的题目都是这样的。
而后面说的就是整体二分,是不带修改的。整体二分的整体,说的就是这个分治其实是把这整个问题都分开了。
在整体二分的过程中,整个问题是真正得被分成了两个问题,两个完全独立的问题。
这个写一题就能理解了

K-th Number

K-th Number

Time Limit: 20000MS Memory Limit: 65536K
Total Submissions: 86706 Accepted: 31344
Case Time Limit: 2000MS

Description

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?"
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 109 by their absolute values --- the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment.

Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

Sample Output

5
6
3

Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

Source

Northeastern Europe 2004, Northern Subregion

给定一个序列,再给若干个询问,每个询问查询一个给定的区间的第k大值

我们考虑对值域二分答案。假设数值a的值域是\([Mina,Maxa]\),第一次二分的值是\(mid=(Mina+Maxa)>>1\)
每次二分,遍历所有的询问\([l_i,r_i]\),对每一个询问统计这个区间里面小于mid的值有多少,记为\(c[i]\)。
我们可以把询问分为两类

​ 1.\(c[i]\leq k[i]\)的
​ 那么说明这个询问的答案在区间\([Mina,mid]\)之间。

 2.$c[i]>k$的
	那么说明这个询问的答案在区间$[mid+1,Maxa]$之间。 

既然我们可以把询问分为这样两类,那不是把数组a也可以分?
按照mid的大小,把a分为两部分,第一个部分是\(a[i]\leq mid\)的
第二个部分是\(a[i]>mid\)的
很明显,第一个部分的询问的答案就在第一个部分的数组中,而第二个部分的询问的答案就在第二个部分的数组中
这样,这就完全的变成了两个问题了,我们不仅二分答案,还二分了数组和询问。所以叫做整体二分

代码不难写,我觉得比CDQ分治好写多了

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <math.h>
#include <string.h>
#define ll long long
using namespace std;
inline int read() {
	char c=getchar();int a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
struct node
{
	int opt,x,y,z;
}q[200001],lq[100001],rq[100001];
int n,m,tot,tr[200001],ans[100001];
const int INF=1e9;
inline int lowbit(int x)
{
	return x&(-x);
}
int ask(int i)
{
	int ans=0;
	while(i>0)
	{
		ans+=tr[i];
		i-=lowbit(i);
	}
	return ans;
}
void add(int i,int x)
{
	while(i<=n)
	{
		tr[i]+=x;
		i+=lowbit(i);
	}
}
void solve(int lval,int rval,int st,int ed)
{
	if(st>ed)return ;
	if(lval==rval)
	{
		for(int i=st;i<=ed;i++)
		{
			if(q[i].opt!=0)ans[q[i].opt]=lval;
		}
		return;
	}
	int mid=(lval+rval)>>1;
	
	int lt=0,rt=0;
	for(int i=st;i<=ed;i++)
	{
		if(q[i].opt==0)
		{
			if(q[i].y<=mid)add(q[i].x,1),lq[++lt]=q[i];
			else rq[++rt]=q[i];
		}
		else
		{
			int cnt=ask(q[i].y)-ask(q[i].x-1);
			if(cnt>=q[i].z)lq[++lt]=q[i];
			else q[i].z-=cnt,rq[++rt]=q[i];
		}
	}
	for(int i=ed;i>=st;i--)
	{
		if(q[i].opt==0&&q[i].y<=mid)add(q[i].x,-1);
	}
	for(int i=1;i<=lt;i++)
		q[st+i-1]=lq[i];
	for(int i=1;i<=rt;i++)
		q[st+i+lt-1]=rq[i];
	solve(lval,mid,st,st+lt-1);
	solve(mid+1,rval,st+lt,ed);
}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++)
	{
		q[++tot].opt=0;
		q[tot].x=i;q[tot].y=read();
	}
	for(int i=1;i<=m;i++)
	{
		int l=read(),r=read(),k=read();
		q[++tot].opt=i;q[tot].x=l,q[tot].y=r,q[tot].z=k;
	}
	solve(-INF,INF,1,tot);
	for(int i=1;i<=m;i++)
	{
		cout<<ans[i]<<endl;
	}
	return 0;
}

这个代码是完全按照蓝书上写的
这个把最开始给的数组当作修改的操作好些是比分开来写更好写一些
学到了

然后就是加强版,如果在中间插入修改操作呢?

Dynamic Rankings

题目描述

给定一个含有 \(n\) 个数的序列 \(a_1,a_2 \dots a_n\),需要支持两种操作:

  • Q l r k 表示查询下标在区间 \([l,r]\) 中的第 \(k\) 小的数
  • C x y 表示将 \(a_x\) 改为 \(y\)

输入格式

第一行两个正整数 \(n,m\),表示序列长度与操作个数。
第二行 \(n\) 个整数,表示 \(a_1,a_2 \dots a_n\)。
接下来 \(m\) 行,每行表示一个操作,都为上述两种中的一个。

输出格式

对于每一次询问,输出一行一个整数表示答案。

样例 #1

样例输入 #1

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

样例输出 #1

3
6

提示

【数据范围】

对于 \(10\%\) 的数据,\(1\le n,m \le 100\);
对于 \(20\%\) 的数据,\(1\le n,m \le 1000\);
对于 \(50\%\) 的数据,\(1\le n,m \le 10^4\);
对于 \(100\%\) 的数据,\(1\le n,m \le 10^5\),\(1 \le l \le r \le n\),\(1 \le k \le r-l+1\),\(1\le x \le n\),\(0 \le a_i,y \le 10^9\)。

请注意常数优化,但写法正常的整体二分和树套树都可以以大约 \(1000\text{ms}\) 每个点的时间通过。

来源:bzoj1901

本题数据为洛谷自造数据,使用CYaRon耗时5分钟完成数据制作。

链接dynamic ranking

这。。好像代码不用改啊?
上面的代码是不是已经可以支持修改了?
第一个循环的顺序就是时间顺序啊,那统计的答案也是这个顺序吧?
只是因为操作是修改,所以要记录每次修改前是什么数值

这个玩意改完就没有东西了,但是不愧是我,还是调了一个小时

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
	char c=getchar();int a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
struct node
{
	int opt,x,y,z,pos;
}q[400001],lq[400001],rq[400001];
int n,m,tot,tr[400001],ans[400001],a[400001],tmp[400001],turn[400001];
const int INF=1e9;
inline int lowbit(int x)
{
	return x&(-x);
}
int ask(int i)
{
	int ans=0;
	while(i>0)
	{
		ans+=tr[i];
		i-=lowbit(i);
	}
	return ans;
}
void add(int i,int x)
{
	while(i<=n)
	{
		tr[i]+=x;
		i+=lowbit(i);
	}
}
void solve(int lval,int rval,int st,int ed)
{
	if(st>ed)return;
	if(lval==rval)
	{
		for(int i=st;i<=ed;i++)
		{
			if(q[i].opt==0)ans[q[i].pos]=lval;
		}
		return;
	}
	int mid=(lval+rval)>>1;
	int lt=0,rt=0;
	for(int i=st;i<=ed;i++)
	{
		if(q[i].opt!=0)
		{
			if(q[i].y<=mid)add(q[i].x,q[i].opt),lq[++lt]=q[i];
			else rq[++rt]=q[i];
		}
		else
		{
			int cnt=ask(q[i].y)-ask(q[i].x-1);
			if(cnt>=q[i].z)lq[++lt]=q[i];
			else q[i].z-=cnt,rq[++rt]=q[i];
		}
	}
	for(int i=ed;i>=st;i--)
	{
		if(q[i].opt!=0&&q[i].y<=mid)add(q[i].x,-q[i].opt);
	}
	for(int i=1;i<=lt;i++)q[st+i-1]=lq[i];
	for(int i=1;i<=rt;i++)q[st+i+lt-1]=rq[i];
	solve(lval,mid,st,st+lt-1);
	solve(mid+1,rval,st+lt,ed);
}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read(),m=read();
	int al=0;
	for(int i=1;i<=n;i++)
	{
		q[++tot].opt=1;
		q[tot].x=i;q[tot].y=a[i]=read();
		tmp[++al]=a[i];
	}
	for(int i=1;i<=m;i++)
	{
		char c;
		cin>>c;
		if(c=='Q')
		{
			int l=read(),r=read(),k=read();
			q[++tot].opt=0;
			q[tot].x=l;
			q[tot].y=r;
			q[tot].z=k;
			q[tot].pos=i;
		}
		else
		{
			int x=read(),y=read();
			q[++tot].opt=-1;
			q[tot].x=x;
			q[tot].y=a[x];
			q[++tot].opt=1;
			q[tot].x=x;
			q[tot].y=y;
			
			a[x]=y;
			tmp[++al]=a[x];
		}
	}
	map<int,int> ma;int tot1=0;
	sort(tmp+1,tmp+1+al);
	for(int i=1;i<=al;i++)
	{
		if(ma[tmp[i]]==0)ma[tmp[i]]=++tot1;
	}
	for(int i=1;i<=tot;i++)
		if(q[i].opt!=0)
			turn[ma[q[i].y]]=q[i].y,q[i].y=ma[q[i].y];
	solve(1,tot1,1,tot);
	for(int i=1;i<=m;i++)
	{
		if(ans[i]!=0)
		{
//			cout<<ans[i]<<' ';
			cout<<turn[ans[i]]<<endl;
		}
	}
	return 0;
}

标签:二分,le,数字,int,分治,整体,tot,++
From: https://www.cnblogs.com/HLZZPawa/p/17965679

相关文章

  • 开关量、数字量、模拟量、离散量和脉冲量它们之间有什么区别?
    开关量、数字量、模拟量、离散量和脉冲量是电子测量和控制系统中经常遇到的不同类型的数据。它们在定义、特性和应用方面存在差异。在电子测量和控制系统设计中,根据实际需求选择合适的数据类型是至关重要的。定义与特点 开关量(SwitchingQuantity)开关量是一种只有两种状态......
  • 汉字数字等多类型字符串中提取数字
    使用正则表达式importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassMain{publicstaticvoidmain(String[]args){Stringinput="你好6.6%";doublenumber=extractNumber(input);System.out.println(n......
  • Photoshop 2024:数字图像处理的行业标准 mac/win版
    Photoshop2024是一款功能强大的数字图像处理软件,被广泛用于创意设计和视觉效果制作。这款软件提供了广泛的工具和功能,使用户能够进行各种复杂的图像编辑和合成工作。→→↓↓载Photoshop2024mac/win版Photoshop2024在图像处理方面具有许多优势。首先,它支持各种图像格式,包括......
  • 数字生成游戏
    数字生成游戏题目描述小明完成了这样一个数字生成游戏,对于一个不包含的数字来说,有以下种生成新的数的规则:将的任意两位对换生成新的数字,例如可以生成;将的任意一位删除生成新的数字,例如可以生成;在的相邻两位之间之间插入一个数字,需要满足。例如可以生成,但......
  • P52 数组中出现次数超过一半的数字
    题目链接:方法一:若数组中有数字的出现次数超过数组长度的一半(绝对众数),则将该数组排序后中间位置的数一定就是该数。classSolution{public:intmoreThanHalfNum_Solution(vector<int>&nums){sort(nums.begin(),nums.end());intn=nums.size();......
  • 20240113-力扣704二分查找
    leetcode链接:https://leetcode.cn/problems/binary-search/solutions/980494/er-fen-cha-zhao-by-leetcode-solution-f0xw/代码随想录链接:https://programmercarl.com/0704.二分查找.html#算法公开课考点:二分查找解决代码:classSolution{publicintsearch(int[]num......
  • 如何把将字符串中的数字转换成数字
    主要采用的是库函数的方法,isdigit,stoi.isdigit可以判断单个字符是否是数字,stoi可以将多个字符(多位数,复数)转换成数字。判断数字可以结合isdigit给出对应的函数。点击查看代码boolisNumber(conststd::string&token){//Checkifthetokenisanumber(posit......
  • python二分法查找
    比如要在列表arr中查找xdeff(arr,x):left=0right=len(arr)whileleft<right:mid=(left+right)//2ifmid<x:left=midelifmid>x:right=midelse:returnmid......
  • 前缀和,二分 - [ABC203D] Pond
    AT_abc203_DPond题意给出一个\(N\timesN\)的矩阵,然后依次枚举\(k\timesk\)的子矩阵。对于\(k\timesk\)的子矩阵,一共有个\(k^2\)元素,找出其中的中位数。这里的中位数是子矩阵中元素从大到小排列的第$\left\lfloor\frac{k^2}{2}\right\rfloor$个元素。问......
  • 无涯教程-LISP - 数字(Numbers)
    CommonLisp number数据类型包括LISP支持的各种数字。LISP支持的数字类型是-IntegerRatiosFloatComplex下图显示了LISP中可用的数字层次结构和各种数字数据类型-数字类型下表描述了LISP中可用的各种数字类型数据-Sr.No.Datatype&描述1fixnum此数据类型表示......