【T】分治与倍增

news/2024/6/17 21:58:49 标签: 算法, c++

分治,分而治之,其中最经典的便是二分

一、二分

一种经典而且非常好用的思想
将原问题对半转换成两个问题,子问题又继续转换成两个问题,许多子问题会很显然对答案没有关系,所以能讲原本O(n)的东西转化为O(logn)
但一般有个条件:单调

(之前讲的快速幂其实也用到的是这类思想)

经典讲法:猜数字

现在有个1-100的数字让你猜,每次会回答你猜的大了还是小了,尽量用最少次数猜出答案
二分实现:每次猜中间的数,然后缩小一般的区间重复操作

#include<bits/stdc++.h>
using namespace std;
int x,a;
int main()
{
	srand(time(0));
	x=rand()%100+1;//x为1-100
	printf("猜1-100的某个数\n");
	while(scanf("%d",&a))
	{
		if(a>x)printf("猜大了\n");
		if(a<x)printf("猜小了\n");
		if(a==x){printf("**对了**\n");return 0;}
	}
}

P2249 【深基13.例1】查找

这个数列是单调不减,所以可以直接用二分来找

#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000005],x;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){scanf("%d",&a[i]);}
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&x);
		/*
		int ans=lower_bound(a+1,a+n+1,x)-a;//二分搜,注意-a
		if(x!=a[ans]) printf("-1 ");//没有,输出-1
		虽然可以用这个自带函数,但我们这里学的是思想,二分思想很重要
		*/
		int l=1,r=n,mid;
		while(l<r)
		{
			mid=(l+r)>>1;
			if(x<=a[mid])r=mid;
			else	     l=mid+1;//等号可能要多思考一下,+1也要思考下
		}
		if(a[l]==x)printf("%d ",l);
		else	   printf("-1 ");
	}
}

P1024 [NOIP2001 提高组] 一元三次方程求解

熟悉一下实数版二分,有时判断的时候可能需要一个eps=1e-3用来辅助判断,因为实数精度 判断有时可能不太准确

#include<bits/stdc++.h>
using namespace std;
double a,b,c,d;
double f(double x)
{
	return a*x*x*x+b*x*x+c*x+d;
}

int main()
{
	scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
	for(int i=-100;i<100;i++)
	{
		double l=i,r=i+1,mid;
		if(f(l)==0){printf("%.2lf ",l);continue;}
		if(f(l)*f(r)<0)
		{
			while(r-l>=0.001)
			{
				mid=(l+r)/2;
				if(f(mid)*f(l)<=0)r=mid;
				else			  l=mid;
				//printf("[%.2lf,%.2lf](%lf)\n",l,r,f(l));
			}
			printf("%.2lf ",l);
		}
		//l为答案
	}
}

P2678 跳石头

二分答案,再学会check对于mid是否成立
需要想到问题对于答案是个单增的,如果mid成立,则>mid也都成立

三分

一般处理单峰函数,不常用的板子
可以看到三分板子题解全在叫你用一堆什么随机算法,单峰函数也不常见,反而随机算法各种题说不定还能对

二、倍增

分治是把问题分开解决,而倍增是从成倍整合解决

ST表

预处理 2 0 2^0 20步转移,然后 2 1 − 2 20 2^1- 2^{20} 21220步分别由之前步整合得到

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int bz[N][20],lg[N];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&bz[i][0]);
	for(int j=1;j<=18;j++)
		for(int i=1;i<=n;i++)
			if(i+(1<<(j-1))<=n)
	bz[i][j]=max(bz[i][j-1],bz[i+(1<<(j-1))][j-1]);//重点精髓语句
	int l,r;
	while(m--)
	{
		scanf("%d%d",&l,&r);
		int p=log2(r-l+1);
		printf("%d\n",max(bz[l][p],bz[r-(1<<p)+1][p]));
	}
}

练习:P1816 忠诚

树上倍增->LCA最近公共祖先

等会建图啊,树相关啊,再回来看(讲),有空的可以先看看学学

#include<bits/stdc++.h>
using namespace std;
const int N=1000009;
int n,q,x,y,nex[N*2],first[N*2],to[N*2],tot=0;
int f[N][21],dep[N];
void Add(int u,int v) //建邻接表
{
	nex[++tot]=first[u];first[u]=tot;to[tot]=v;
	nex[++tot]=first[v];first[v]=tot;to[tot]=u;
}
void Init(int u,int father) //预处理,father 是 u 的父节点
{
	dep[u]=dep[father]+1;
	for(int i=0;i<=19;i++) //预处理出从某个点跳 2 的 i 次方到达的位置
	f[u][i+1]=f[f[u][i]][i];
	for(int e=first[u];e;e=nex[e]) //枚举每一条与 u 相连的点
	{
		int v=to[e];
		if(v==father) continue; //如果这条连向父节点,就 continue 
		f[v][0]=u; //v 的父亲是 u 
		Init(v,u); //递归
	}
}
int Lca(int x,int y) //找 LCA 
{
	if(dep[x]<dep[y]) swap(x,y); //保证 x 深度更大
	for(int i=20;i>=0;i--) //使它们俩跳至深度相同
	{
		if(dep[f[x][i]]>=dep[y]) x=f[x][i];
		if(x==y) return x; //属于 x、y 在一条链上,y 是 x 和 y 的最近公共祖先
	}
	for(int i=20;i>=0;i--) //在 x 和 y 深度相同的情况下
		if(f[x][i]!=f[y][i]) //目标位置不相等,x 和 y 就往上爬
		{
			x=f[x][i]; //x 往上爬
			y=f[y][i]; //y 往上爬
		}
	return f[x][0]; //最后肯定一起跳到了 lca 的下面一个
}
int dist(int x,int y){return dep[x]+dep[y]-2*dep[Lca(x,y)];} //求距离
int main()
{
	scanf("%d",&n);
	scanf("%d",&q);
	int st;
	scanf("%d",&st);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		Add(x,y);
	}
	Init(st,0); //预处理
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&x,&y);
		printf("%d\n",Lca(x,y));
	}
	return 0;
}

http://www.niftyadmin.cn/n/5136132.html

相关文章

线性代数 第一章 行列式

一、概念 不同行不同列元素乘积的代数和&#xff08;共n!项&#xff09; 二、性质 经转置行列式的值不变&#xff0c;即&#xff1b; 某行有公因数k&#xff0c;可把k提到行列式外。特别地&#xff0c;某行元素全为0&#xff0c;则行列式的值为0&#xff1b; 两行互换行列式…

研究生安排

研一 看论文不懂先记着 论文一定多看的&#xff0c;建议300篇论文&#xff0c;500最好。 选题的时候要心里有谱&#xff0c;先找小论文&#xff0c;再找大论文 研二 确定研究方向和目标 和老师切磋研究的是什么 明确要做的东西是什么&#xff0c;是否已经明确要做什么&#xff…

【音视频|wav】wav音频文件格式详解

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

【Vue】初步认识<script setup>语法糖和组合式 API

▒ 目录 ▒ &#x1f6eb; 导读需求开发环境 1️⃣ &#x1f6eb; 导读 需求 最近写代码的时候&#xff0c;发现<script setup>这样的代码&#xff0c;没见过&#xff0c;好奇&#xff0c;想知道。 所以就有了这篇文章。 很多文章都说setup是vue3的特权。但是&#xff…

怎么搭建一个蛋糕店小程序?

在当今的移动互联网时代&#xff0c;很多企业纷纷选择了小程序作为推广和销售的利器。对于蛋糕店来说&#xff0c;创建一个小程序可以提高品牌知名度&#xff0c;增加销售渠道。下面&#xff0c;我们以【乔拓云】第三方平台为例&#xff0c;来介绍一个完整蛋糕店小程序的制作流…

【Linux】多路IO复用技术①——select详解如何使用select模型在本地主机实现简易的一对多服务器(附图解与代码实现)

这一篇的篇幅可能有点长&#xff0c;但真心希望大家能够静下心来看完&#xff0c;相信一定会有不小的收获。那么话不多说&#xff0c;我们这就开始啦&#xff01;&#xff01;&#xff01; 目录 一对一服务器中的BUG 如何实现简易的一对多服务器 实现简易一对多服务器的大体…

修改el-date-picker宽度

<div style"width: 100%"><el-date-pickerstyle"width:100%"v-model"value"type"datetimerange"start-placeholder"开始日期"end-placeholder"结束日期":default-time"[12:00:00]"value-forma…

操作系统(Linux)外壳程序shell 、用户、权限

文章目录 操作系统和shell外壳Linux用户普通用户的创建和删除用户的切换 Linux 权限Linux 权限分类文件访问权限修改文件的权限权限掩码粘滞位 大家好&#xff0c;我是纪宁。 这篇文章将介绍 Linux的shell外壳程序&#xff0c;Linux用户切换机Linux权限的内容。 操作系统和shel…