首页 > 其他分享 >动态dp & 矩阵加速递推

动态dp & 矩阵加速递推

时间:2024-08-19 19:38:10浏览次数:5  
标签:limits tr 矩阵 mt max 递推 dp

广义矩阵乘法

我们定义两个矩阵 \(A,B\) 在广义矩阵乘法下的乘积为 \(C\),其中

\[C = \begin{bmatrix} \max\limits_{i=1}\limits^{m} A_{1,i} + B_{i,1} & \max\limits_{i=1}\limits^{m} A_{1,i} + B_{i,2} & \dots & \max\limits_{i=1}\limits^{m} A_{1,i} + B_{i,k} \\\ \sum\limits_{i=1}\limits^{m} A_{2,i} + B_{i,1} & \max\limits_{i=1}^{m} A_{2,i} + B_{i,2} & \dots & \max\limits_{i=1}\limits^{m} A_{2,i} + B_{i,k} \\\ \vdots & \vdots & \ddots & \vdots \\\ \max\limits_{i=1}\limits^{m} A_{n,i} + B_{i,1} & \max\limits_{i=1}\limits^{m} A_{n,i} + B_{i,2} & \dots & \max\limits_{i=1}\limits^{m} A_{n,i}+ B_{i,k}\end{bmatrix}\]

这么定义矩阵乘法是为了改写某些 DP 柿子。不难发现这个乘法依然具有结合律。

动态 dp

引入

有一个序列 \(a\),你可以在其中选择一些数,但是你不能选择相邻的两个数,求你能选出的数的总和最大是多少。

我们令 \(dp_{i,0/1}\) 为考虑前 \(i\) 个数,选或不选第 \(i\) 个数的最大和。

不难得到 \(dp_{i,0}=\max(dp_{i-1,0},dp_{i-1,1}),dp_{i,1}=dp_{i-1,0}+a_i\)。

那么这跟我们上面所说的矩阵乘法有什么关系呢?

我们将 dp 式改写一下:

\[dp_{i,0}=\max(dp_{i-1,0}+0,dp_{i-1,1}+0) \]

\[dp_{i,1}=\max(dp_{i-1,0}+a_i,dp_{i-1,1} - \infty) \]

现在是不是和上述的广义矩阵乘法很像了?我们将 dp 继续改写为矩阵乘的形式:

\[\begin{bmatrix} dp_{i-1,0} & dp_{i-1,1} \end{bmatrix} \times \begin{bmatrix} 0 & a_i \\\ 0 & - \infty \end{bmatrix} = \begin{bmatrix} dp_{i,0} & dp_{i,1} \end{bmatrix} \]

由于矩阵乘具有结合律,所以我们现在可以将 dp 结果写成一系列矩阵连乘的结果了!

但是我们这么做却不是为了优化时间复杂度,而是为了:

带修

如果我们将引入题改一下,增加 \(m\) 次单点修改 \(a_i\) 的值,怎么做?

我们只需要使用线段树维护上述的矩阵乘法即可!!!

例题 Gym102644H String Mood Updates

首先我们考虑暴力 dp。

我们令 \(dp_{i,0/1}\) 代表读完前 \(i\) 个字符后当前状态为 \(0/1\) 的方案数。转移有:

\[\begin{cases}dp_{i,0}=dp_{i-1,0}+dp_{i-1,1},dp_{i,1}=0 \space\space s_i=\texttt{S,D} \\\ dp_{i,0}=0,dp_{i,1}=dp_{i-1,0}+dp_{i-1,1} \space\space s_i=\texttt{H} \\\ dp_{i,0}=dp_{i-1,1},dp_{i,1}=dp_{i-1,0} \space\space s_i=\texttt{A,E,I,O,U} \\\ dp_{i,0}=20 \times dp_{i-1,0} + 7 \times dp_{i-1,1},dp_{i-1,1}=6 \times dp_{i-1,0} + 19 \times dp_{i-1,1} \space\space s_i=\texttt{?} \\\ dp_{i,0}=dp_{i-1,0},dp_{i,1}=dp_{i-1,1} \space\space \operatorname{otherwise}\end{cases} \]

带修的话,根据这个状态转移,构建出转移矩阵,并用线段树维护即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
#define fi first
#define sc second
#define pii pair<int,int>
#define pb push_back
const int maxn=2e5+10;
const int mod=1e9+7;
int n,m,dp[maxn][2];
string s;
struct mat{
	int n,a[3][3];
	void init(int x){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++) a[i][j]=x;
		}
	}
	void getI(){
		init(0);
		for(int i=1;i<=n;i++) a[i][i]=1;
	}
	mat operator *(mat x){
		mat ans;
		ans.n=n,ans.init(0);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				for(int k=1;k<=n;k++){
					ans.a[i][j]=(ans.a[i][j]+a[i][k]*x.a[k][j])%mod;
				}
			}
		}
		return ans;
	} 
	void output(){
		for(int i=1;i<=n;i++,cout<<endl){
			for(int j=1;j<=n;j++) cout<<a[i][j]<<" ";
		}
	}
};
struct node{
	int l,r;
	mat mt;
	node operator +(node x){
		if(l==-1) return x;
		if(x.l==-1) return (*this);
		node res;
		res.l=l,res.r=x.r,res.mt=(mt*x.mt);
		return res;
	}
	void debug(){
		cout<<l<<" "<<r<<endl;
		cout<<"MATRIX:"<<endl;
		mt.output();
		cout<<endl;
	}
}tr[maxn*4];
int ls(int u){
	return (u<<1);
}
int rs(int u){
	return (u<<1)|1;
}
bool ir(int L,int R,int l,int r){
	return (L<=l)&&(r<=R);
}
bool ofr(int L,int R,int l,int r){
	return (R<l)||(r<L);
}
void pushup(int u){
	if(tr[u].l==tr[u].r) return ;
	tr[u]=tr[ls(u)]+tr[rs(u)];
}
void build(int u,int l,int r){
	tr[u].l=l,tr[u].r=r,tr[u].mt.n=2;
	if(l==r){
		if(s[l]=='S'||s[l]=='D'){
			tr[u].mt.a[1][1]=1,tr[u].mt.a[1][2]=0;
			tr[u].mt.a[2][1]=1,tr[u].mt.a[2][2]=0;
		}
		else if(s[l]=='H'){
			tr[u].mt.a[1][1]=0,tr[u].mt.a[1][2]=1;
			tr[u].mt.a[2][1]=0,tr[u].mt.a[2][2]=1;
		}
		else if(s[l]=='A'||s[l]=='E'||s[l]=='I'||s[l]=='O'||s[l]=='U'){
			tr[u].mt.a[1][1]=0,tr[u].mt.a[1][2]=1;
			tr[u].mt.a[2][1]=1,tr[u].mt.a[2][2]=0;
		}
		else if(s[l]=='?'){
			tr[u].mt.a[1][1]=20,tr[u].mt.a[1][2]=6;
			tr[u].mt.a[2][1]=7,tr[u].mt.a[2][2]=19;
		}
		else{
			tr[u].mt.a[1][1]=1,tr[u].mt.a[1][2]=0;
			tr[u].mt.a[2][1]=0,tr[u].mt.a[2][2]=1;
		}
		return ;
	}
	int mid=(l+r)>>1;
	build(ls(u),l,mid),build(rs(u),mid+1,r),pushup(u);
}
void upd(int u,int x,char k){
	if(tr[u].l==tr[u].r){
		if(k=='S'||k=='D'){
			tr[u].mt.a[1][1]=1,tr[u].mt.a[1][2]=0;
			tr[u].mt.a[2][1]=1,tr[u].mt.a[2][2]=0;
		}
		else if(k=='H'){
			tr[u].mt.a[1][1]=0,tr[u].mt.a[1][2]=1;
			tr[u].mt.a[2][1]=0,tr[u].mt.a[2][2]=1;
		}
		else if(k=='A'||k=='E'||k=='I'||k=='O'||k=='U'){
			tr[u].mt.a[1][1]=0,tr[u].mt.a[1][2]=1;
			tr[u].mt.a[2][1]=1,tr[u].mt.a[2][2]=0;
		}
		else if(k=='?'){
			tr[u].mt.a[1][1]=20,tr[u].mt.a[1][2]=6;
			tr[u].mt.a[2][1]=7,tr[u].mt.a[2][2]=19;
		}
		else{
			tr[u].mt.a[1][1]=1,tr[u].mt.a[1][2]=0;
			tr[u].mt.a[2][1]=0,tr[u].mt.a[2][2]=1;
		}
		return ;
	}
	int mid=(tr[u].l+tr[u].r)>>1;
	if(x<=mid) upd(ls(u),x,k);
	else upd(rs(u),x,k);
	pushup(u);
}
void solve(){
	cin>>n>>m>>s,s=" "+s;
	build(1,1,n);
//	tr[1].mt.output();
	cout<<tr[1].mt.a[2][2]<<endl;
	while(m--){
		int x;
		char c;
		cin>>x>>c,upd(1,x,c);
//		tr[1].mt.output();
		cout<<tr[1].mt.a[2][2]<<endl;
//		for(int i=1;i<n*2;i++) tr[i].debug();
//		cout<<endl;
	}
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int t=1;
//    cin>>t;
    while(t--) solve();
    return 0;
}
/*
Samples
input:

output:

THINGS TODO:
检查freopen,尤其是后缀名
检查空间
检查调试语句是否全部注释
*/

标签:limits,tr,矩阵,mt,max,递推,dp
From: https://www.cnblogs.com/luqyou/p/18367959

相关文章

  • tcp与udp的总结+connect阻塞+tcp三次握手、四次挥手+常见的服务器IO(发送数据+接收数
    一,TCP与UDP的基本总结TCP(传输控制协议)和UDP(用户数据报协议)是两种主要的传输层协议。TCP是面向连接的,提供可靠、顺序的传输,适用于需要高可靠性的应用,如网页浏览和文件传输。它通过重传机制和流量控制确保数据完整性。UDP是无连接的,速度快但不保证数据的可靠性和顺序,适用于对实时性......
  • 03-Matlab数组与矩阵
    数组的建立和操作数组算术运算数组信息获取矩阵的建立矩阵的扩展矩阵的块操作矩阵中元素的删除赋值为一对方括号矩阵的转置加点不转置为共轭复数没点的转置为共轭复数矩阵的旋转矩阵的翻转矩阵尺寸的改变矩阵加减法矩阵乘法矩阵除法矩阵中元素......
  • 预设性DP
    预设性DP从最简单的起DP搬运工2Description给你n,Kn,Kn,K,求有多少个111到nnn的排列,恰好有KKK个数i(1<i<n)i(1<i<n)i(1<i<n)满足ai−1,ai+1a_{i-1},a_{i+1}ai−1​,ai+1​都小于aia_iai​。InputFormat一行两个整数n,Kn,Kn,K。OutputFormat一行一个整数......
  • 【面试】阐述TCP和UDP的区别
    面试模拟场景面试官:你能阐述一下TCP和UDP的区别吗?###参考回答示例1.连接性TCP:面向连接(Connection-Oriented):TCP是一种面向连接的协议,在传输数据之前需要建立连接。在TCP通信过程中,客户端和服务器之间通过“三次握手”来建立连接,然后再进行数据传输,确保两者之间的......
  • 基于django+vue框架的实时新闻推送平台edpjq【开题报告+程序+论文】计算机毕设
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景在信息爆炸的时代,新闻资讯的时效性成为了媒体竞争的关键。随着互联网技术的飞速发展,人们获取新闻的方式已从传统的报纸、电视转向了手机、......
  • 矩阵和神经网络的优雅与力量-《Python神经网络编程》读后感
    《Python神经网络编程》是一本非常优秀的神经网络入门编程书,作者手把手从安装环境开始,每一行代码都是在树莓派上就能运行的,甚至可以说不需要什么第三方库,仅仅用了矩阵的优雅和力量,就能够在树莓派上顺利的运行。仅仅是这样简单的代码实现,就实现了神经网络的前馈信号计算、误差......
  • 矩阵加速线性递推
    前置:【模板】矩阵快速幂斐波那契数列定义\(Fib_i\)为斐波那契数列第\(i\)项,斐波那契数列定义如下\[Fib_n=\left\{\begin{aligned}1\space(n\le2)\\Fib_{n-1}+Fib_{n-2}\space(n\ge3)\end{aligned}\right.\]给定一个\(n\),求出\(Fib_n\mod10^9+7\)的值。......
  • 树形 dp
    在树上运行的dp叫做树形dp。题单。树形dp入门问题例1.1:没有上司的舞会我们发现,对于一个节点,要么选或不选,且题目中要求求出权值最大的方案,不妨分别设\(dp_{i,0},dp_{i,1}\)为在以\(i\)为根的子树中,第\(i\)个点选或不选情况下的最大权值。那么可以得到\[dp_{i,0}=......
  • 状压 dp
    定义在动态规划中,可能存在以“集合”为状态的动态规划,应为集合不易表示,所以通常用一个二进制数来表示集合。具体的,二进制数的第\(i\)位即表示当前集合是否包含第\(i\)个元素。技巧因为许多位运算的运算优先级很迷,所以搞不明白就尽量用括号。二进制操作将\(n\)位二进......
  • @Async使用ThreadPoolTaskExecutor 多线程
    SpringBoot中的线程池ThreadPoolTaskExecutor,@Async的使用线程池@Configuration@EnableAsyncpublicclassExcutorConfig{@Bean(name="ThreadPoolTaskExecutor")publicThreadPoolTaskExecutorThreadPoolTaskExecutor(){ThreadPoolTaskExecutorex......