电子学会八级-数据结构-倍增

倍增思想可以很早学习,有些需要数学基础,所以放到这个级别

应用--二进制拆分

应用一 快速幂

应用二-快速乘
O(1)快速乘
https://www.luogu.com.cn/problem/T105910

#include<bits/stdc++.h>
#define ll long long
using namespace std;

//二进制实现快速乘 
/*
	偶数相乘  奇数加入ans
	8*6=16*3=32*1+16=16+32=48
	              16先加入ans  最后一次b=1时 ans=ans+a =16+32=48 
*/
ll mul_q(ll a,ll b,ll mod){
	long long res=0;
	while(b){//b>=1指向如下操作 最后一次=1时 把res=res+a 
		if(b&1)//与1位运算 奇数为1 true 偶数为0 false
			res=(res+a)%mod;//奇数时加入ans
		a=(a<<1)%mod;//a*2 
		b>>=1;// b/2 
	}
	return res;
}

int main(){
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans=mul_q(a,b,p);
    cout<<ans<<endl;
    return 0;
}

猜数字
https://www.luogu.com.cn/problem/P3868

多少个1
https://www.luogu.com.cn/problem/P4884

快速阶乘算法
https://www.luogu.com.cn/problem/P5282
随机数生成器
https://www.luogu.com.cn/problem/P2044

应用三-矩阵乘法
斐波那契数列
https://www.luogu.com.cn/problem/P1962

#include<bits/stdc++.h>
typedef long long ll; 
const ll mod=1e9+7;
using namespace std;
 
/*
https://loj.ac/p/10219
https://matrix.reshish.com/multCalculation.php
2行3列 * 3行2列 =2行2列 
input
2 3
1 2 3
3 2 1
2
1 1
2 2
3 3
output
14 14
10 10
*/ 
struct Matrix{
	ll g[3][3];
	Matrix(){//构造函数 初始矩阵数据都为0 
		memset(g,0,sizeof(g));	
	}//矩阵初始化为0 
	
	Matrix operator *(const Matrix &b) const{//重载乘号 
		Matrix res;//构造新矩阵 并通过构造函数初始化矩阵数据都为0 
		for(int i=1;i<=2;i++){
			for(int j=1;j<=2;j++){
				for(int k=1;k<=2;k++){
					//两矩阵相乘 必须满足第2矩阵行数=第2矩阵列数 
					//计算对应两个矩阵单元格 累加到结果矩阵
					//新矩阵行有第一个矩阵行决定  列有第2个矩阵列决定 
					res.g[i][k]=(res.g[i][k]+g[i][j]*b.g[j][k]%mod)%mod;
				}
			}
		}
		return res;
	}
}a,ans;

/*
a
1  1
1  0

ans 初始化f[1]+f[2] 
1  1
0  0
*/ 
void init(){
	a.g[1][1]=1,a.g[1][2]=1,a.g[2][1]=1;
	ans.g[1][1]=ans.g[1][2]=1;
}

//x为矩阵相乘次数 参考普通快速幂 
void qpow(ll x){//矩阵快速幂 
	while(x){
		if(x&1) ans=ans*a;//奇数 累乘到ans中 
		a=a*a;//去除偶数后 可以按幂乘 a*a 
		x>>=1;//x=x/2  
	}
}
/*
f[i]  = 1*f[i-1] +   1*f[i-2]
f[i-1]= 1*f[i-1] +   0*f[i-2]

所以 *代表矩阵乘 矩阵乘可以使用乘法结合率  所以可以如下1  1矩阵 
f[i]   =     1  1   *   f[i-1]     =  1  1    *   1  1    *  f[i-2] 
f[i-1]       1  0       f[i-2]        1  0        1  0       f[i-3]
*/
 
int main(){
	ll n;
	cin>>n;
	if(n<=2){
		cout<<1;
		return 0;
	}
	init();//初始数据 
	/*
	f(1) f(2)项和 * 一个矩阵n-2次        f(n)+f(n-1) 
	f[1]    *       1  1                 f[n] 
	f[2]            1  0                 f[n-1]
	所以对矩阵就行n-2次相乘  使用快速幂降低时间复杂度 
	*/
	qpow(n-2);
	cout<<ans.g[1][1]%mod;//输出第f[n]项结果 
	return 0;
}

矩阵快速幂
https://www.luogu.com.cn/problem/P3390
【模板】矩阵加速(数列)
https://www.luogu.com.cn/problem/P1939
矩阵乘法
https://www.luogu.com.cn/problem/B2105
【模板】矩阵乘法
https://www.luogu.com.cn/problem/T103763
[国家集训队]矩阵乘法
https://www.luogu.com.cn/problem/P1527

应用四
ST算法
【模板】ST 表
https://www.luogu.com.cn/problem/P3865

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+5;
//f[i][j]表示第i个开始 到i + (2的j次方)的最大值 
int f[maxn][50],lc,n,m,L,R;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){//初始化 
		scanf("%d",&f[i][0]);
	}
//	lc=(int)(log(n)/log(2));//logn 以2为底  n的对数作为 
	lc=log2(n);//以2为底  n的对数  列 
	for(int j=1;j<=lc;j++){//动态规划创建st表 
		for(int i=1;i<=n-(1<<j)+1;i++){//n-(1<<j)+1保证i 不出界 
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);//计算左右边界 
		}
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d",&L,&R);
//		int p=int(log(R-L+1)/log(2));
		int p=log2(R-L+1); //可计算范围
		// f[L][p]  f[R-(1<<p)+1][p] 
		printf("%d\n",max(f[L][p],f[R-(1<<p)+1][p]));//x+(1<<p)-1 = r  x=r-(1<<p)+1
	}
}

质量检测
https://www.luogu.com.cn/problem/P2251

Balanced Lineup G
https://www.luogu.com.cn/problem/P2880

理想的正方形
https://www.luogu.com.cn/problem/P2216

01 序列
https://www.luogu.com.cn/problem/P7809

区间与除法
https://www.luogu.com.cn/problem/P5629

应用五
LCA 最近公共祖先

【模板】最近公共祖先(LCA)
https://www.luogu.com.cn/problem/P3379

#include <bits/stdc++.h>
using namespace std;

const int N  =  500005, M = N * 2;
struct Edge {
    int v, nxt;//终点 nxt下一节点 
}e[M];//链式前向星存储 

int n, m, s, hd[N], ecnt;
//fa[i][j]从树上编号为 i 的节点向上走2的j次方步会走到哪个节点
//dep[i] i节点的深度 
int dep[N], fa[N][22], lg[N];

//链式前向星添加边 
void add(int u, int v) {
	e[++ecnt].v = v;
	e[ecnt].nxt = hd[u];
	hd[u] = ecnt;
}
//各点的深度和u跳2的j次方的节点 
//u 当前节点 fath父节点 
void dfs(int u, int fath) {
	fa[u][0]=fath;//走2的0次方 即1步走到父节点 
	dep[u]=dep[fath]+1;//u 节点深度为父节点深度+1 
	for(int i=1;i<=lg[dep[u]];i++)//lg=log2  循环深度对数 倍增几步走完 
		fa[u][i]=fa[fa[u][i-1]][i-1];//fa[u][i-1]走一半到达节点q fa[q][i-1]在q基础上继续完成另一半 
	for(int i=hd[u];i;i=e[i].nxt){//链式前向星 逐层出来 
		if(e[i].v==fath) continue;//当前节点为父节点即根节点 结束 
		dfs(e[i].v,u);//继续往下处理 
	}
}

//x y的最近公共祖先 
int lca(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);// x y交换 方便后续逻辑统一处理 
	while(dep[x] > dep[y])
		x = fa[x][lg[dep[x]-dep[y]]];//每次跑2的lg[dep[x]-dep[y]]个节点 一直跳到深度相同 
	if(x == y) return x;//如果 y正好跳到x点 x即为x y的最近公共祖先 
	for(int k = lg[dep[x]]; k >= 0; k--)//不断向上跳(lg就是之前说的常数优化)
		if(fa[x][k] != fa[y][k])//因为我们要跳到它们LCA的下面一层,所以它们肯定不相等,如果不相等就跳过去
			x = fa[x][k], y = fa[y][k];//x y分别跳相同级数 
	return fa[x][0];//父节点即为最近公共祖先 
}

int main(){
	scanf("%d %d %d", &n, &m, &s);//n节点的个数 m询问的个数 s根节点编号 
	for(int i=1;i<=n-1;i++){//n-1条边 存储在链式前向星 
		int u, v;
		scanf("%d %d",&u,&v);
		add(u,v);
		add(v,u);//无向边 
	}
	for(int i=2;i<=n;i++)//基础log2 存储 保证O(1)获取 
		lg[i]=lg[i/2]+1;
	dfs(s,0);//从根节点开始记录每个节点深度和每个节点跳2的j次方后到达的节点 
	for(int i=1;i<=m;i++) {
		int x,y;
		scanf("%d %d",&x,&y);//输入x y 
		printf("%d\n",lca(x,y));//输出 x y的最近公共祖先 
	}
	return 0;
}

Milk Visits S
https://www.luogu.com.cn/problem/P5836
会议
https://www.luogu.com.cn/problem/P1395
仓鼠找 sugar
https://www.luogu.com.cn/problem/P3398
紧急集合/聚会
https://www.luogu.com.cn/problem/P4281
小猪佩奇爬树
https://www.luogu.com.cn/problem/P5588
倍增floyd,倍增FFT、后缀数组等倍增思想

posted @ 2022-05-01 10:42  new-code  阅读(35)  评论(0编辑  收藏  举报