矩阵乘法优化DP复习
前言
最近做毒瘤做多了……联赛难度的东西也该复习复习了。
Warning:本文较长,难度分界线在“中场休息”部分,如果只想看普及难度的可以从第五部分直接到注意事项qwq
文中用(比如现在这个文本)引用文本书写的部分为总结性内容,即使是跳过部分也建议阅读awa
没事,最难也就NOI2020的签到题,不怕(
0——P1962 斐波那契数列
题意
\\
n>2,F(n)=F(n-1)+F(n-2).
\]
对于上述递推式,求 \(F(n)\mod 1e9+7\) ,给出的 \(n\leq 2^{63}\) .
思路
递推式给你了。照理讲 \(O(n)\) 是多么优秀的复杂度啊 然而并不。
于是,对于一个清晰明白的递推式,考虑矩乘。
当然你要上生成函数也没人拦你,不过有一说一斐波那契的生成函数不咋好看(
首先是答案矩阵:
\begin{pmatrix}
f(n) & f(n-1)\\
\end{pmatrix}
\]
然后是转移矩阵。转移矩阵 \(base\) 可以通过:
\]
来确定。保险一点就是设出每个矩阵中的数,然后根据每个项解方程即可 当然也可以看着递推式直接手推 。
但是我个人比较喜欢通过矩阵意义推理。
首先,根据矩阵乘法 c[i][j]=a[i][k]*b[k][j] ,可以知道, \(base\) 的第一行就是 \(F(n)\) 第一列也就是 \(f(n)\) 对下一个答案矩阵的两列的贡献。
具体地讲,\(base\) 的第一行就是 \(f(n)\) 分别对 \(F(n+1)\) 中 \(f(n+1)\) (第一列)和 \(f(n)\) (第二列)贡献了 \(1\times f(n)\) .\(f(n-1)\) 同理。
最后的 \(base\) :
1 & 1\\\\
1 & 0\\\\
\end{pmatrix}
\]
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
struct matrix
{
        ll mt[5][5];
        matrix() { memset(mt,0,sizeof(mt)); }
        matrix  operator * ( matrix b )
        {
                matrix c;
                for ( int i=1; i<=2; i++ )
                 for ( int j=1; j<=2; j++ )
                  for ( int k=1; k<=2; k++ )
                        c.mt[i][j]=(c.mt[i][j]+(mt[i][k]*b.mt[k][j])%mod)%mod;
                return c;
        }
}ans,base;
matrix power( ll b )
{
        matrix res=ans,a=base;
        for ( ; b; b>>=1,a=a*a )
                if ( b&1 ) res=res*a;
        return res;
}
int main()
{
        ll n; scanf( "%lld",&n );
        if ( n<=2 ) { printf( "1\n"); return 0; }
        ans.mt[1][1]=1; ans.mt[1][2]=1; ans.mt[2][1]=0; ans.mt[2][2]=0;
        base.mt[1][1]=1; base.mt[1][2]=1; base.mt[2][1]=1; base.mt[2][2]=0;
        ans=power(n-2);
        printf( "%lld\n",ans.mt[1][1] );
}
1——P1939 【模板】矩阵加速(数列)
题意
\\
x>3,a_x=a_{x-1}+a_{x-3}
\]
求 \(a_n\mod 1e9+7\)
思路
推导过程同上,把 \(f(n+1)\) 拆成递推式即可。
答案矩阵:
\begin{pmatrix}
f(n),f(n-1),f(n-2)
\end{pmatrix};
\]
下一个:
\begin{pmatrix}
f(n)+f(n-2),f(n),f(n-1)
\end{pmatrix}
\]
转移矩阵:
\begin{pmatrix}
1 & 1 & 0\\\\
0 & 0 & 1\\\\
1 & 0 & 0\\\\
\end{pmatrix}
\]
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
struct matrix
{
        ll mt[5][5];
        matrix() { memset(mt,0,sizeof(mt)); }
        matrix  operator * ( matrix b )
        {
                matrix c;
                for ( int i=1; i<=3; i++ )
                 for ( int j=1; j<=3; j++ )
                  for ( int k=1; k<=3; k++ )
                        c.mt[i][j]=(c.mt[i][j]+(mt[i][k]*b.mt[k][j])%mod)%mod;
                return c;
        }
}ans,base;
matrix power( ll b )
{
        matrix res=ans,a=base;
        for ( ; b; b>>=1,a=a*a )
                if ( b&1 ) res=res*a;
        return res;
}
int main()
{
        int T; scanf( "%d",&T );
        ans.mt[1][1]=1; ans.mt[1][2]=1; ans.mt[1][3]=1;
        base.mt[1][1]=1; base.mt[1][2]=1; base.mt[2][3]=1; base.mt[3][1]=1;
        while ( T-- )
        {
                ll n; scanf( "%lld",&n );
                if ( n<=3 ) { printf( "1\n"); continue; }
                matrix res=power( n-3 );
                printf( "%lld\n",res.mt[1][1] );
        }
}
2——HDU5171 GTY's birthday gift
题意
有一个大小为 \(n\) 的可重集 \(S\) ,小奇每次操作可以加入一个数 \(a+b(a,b\in S)\) ,求 \(k\) 次操作后可获得的 \(S\) 的和的最大值。
思路
关键在于求和。涉及到了:
矩阵加维
- 带常数项
 
\(f(n)=f(n−1)+f(n−2)+k\)
答案矩阵: \(F(n)=(f(n),f(n-1),k)\)
- 带未知项
 
$f(n)=f(n−1)+f(n−2)+n $
答案矩阵: \(F(n)=(f(n),f(n-1),n,1)\)
- 求和
 
\(f(n)=f(n-1)+f(n-2),S(n)=\sum_{i=1}^nf(i)\)
答案矩阵: \(F(n)=(f(n),f(n-1),S(n))\)
显然此题属于第三种情况。为什么能递推呢,原因在于要让最后的和最大,又是个可重集,显然每次贪心选最大和次大就好了。
答案矩阵:
\begin{pmatrix}
f(n),f(n-1),S(n)
\end{pmatrix}
\]
下一个:
\begin{pmatrix}
f(n)+f(n-1),f(n),S(n)+f(n)+f(n-1)
\end{pmatrix}
\]
转移矩阵:
\begin{pmatrix}
1 & 1 & 1\\\\
1 & 0 & 1\\\\
0 & 0 & 1\\\\
\end{pmatrix}
\]
Tips
(负数这个据说 HDU上面没有,但是BZOJ是有的)
当最大和次大都是负数,这两个的和累加 k 次即可。
若次大是负数,那么先把最大和次大累加直到次大大于0(每次k--),然后正常矩乘即可。
(这道题还要注意不同寻常的模数……)
还有就是我代码在HDU上面死活过不去,TLE;然后就跑到(黑暗)BZOJ上去交了,去掉了多测,加上了负数之后不用快读 193ms 跑得飞快……要交哪个OJ自己定吧。(主要dark上面有数据下载,虽然我没用)
代码
//-----------矩乘部分省略,同上-------------------
ll val[100010];
int main()
{
        ll n,k,tot=0;
        scanf( "%lld%lld",&n,&k );
        for ( ll i=1; i<=n; i++ )
                        scanf( "%lld",&val[i] ),tot+=val[i],tot%=mod;
        sort( val+1,val+1+n );
        if ( val[n]<=0 )
        {
                tot+=(val[n]+val[n-1])*k; printf( "%lld\n",tot%mod );  return 0;
        }
        while ( val[n-1]<0 && k ) val[n-1]=(val[n]+val[n-1])%mod,tot=(tot+val[n-1])%mod,k--;
        ans.mt[1][1]=val[n]; ans.mt[1][2]=val[n-1]; ans.mt[1][3]=tot;
        base.mt[1][1]=1; base.mt[1][2]=1; base.mt[1][3]=1; base.mt[2][1]=1; base.mt[2][3]=1; base.mt[3][3]=1;
        matrix res=power( k );
        printf( "%lld\n",res.mt[1][3] );
}
3——Power of Matrix(UVA11149)
题意
给定 \(n,k,A(n\times n)\) ,求 \(A+A^2+\dots+A^k\) 的矩阵个位(就是矩阵每个元素都是一位,也就是 \(\mod =10\) .
思路
终于不用推转移矩阵了(
采用分治的思想。记 \(P_k=A+A^2+\dots +A^k\) ,对 \(k\) 的奇偶性进行讨论:
\(k\mod 2==0\) ,那么 原式 \(=(1+P_{k/2} )\times P_{k/2}\);
\(k\mod 2==1\),那么可以转化为 \(A+A\times P_{k-1}\) ,回到第一种情况。
代码
惊奇地发现我五个月前写过这道题 为了复习还是再写一遍。
有一说一,这道题练矩阵运算板子还是不错的qwq,但是要知道 UVA 和 LA 对输出的要求之高简直一言难尽……拼命搞多测和严格的格式要求……作为刷训练指南的人已经习惯了(各位加油
然后,结构体内函数如果 没有类型且和结构体同名 ,是一种奇妙的写法,就相当于你每建一个新的结构体会自动运行这部分代码,可以用来初始化。如果你传参数(比如 (int a=0) ),就是默认 a=0 ,如果你写了别的参数就是你写的那个值,这样可以自定义初始化(这题不用)。
//主要部分
struct matrix
{
        ll mt[50][50];
        matrix() { memset( mt,0,sizeof(mt) ); }
        matrix operator + ( const matrix &b )
        {
                matrix c;
                for ( int i=1; i<=n; i++ )
                 for ( int j=1; j<=n; j++ )
                        c.mt[i][j]=(mt[i][j]+b.mt[i][j])%mod;
                return c;
        }
        matrix operator * ( const matrix &b )
        {
                matrix c;
                for ( int i=1; i<=n; i++ )
                 for ( int j=1 ;j<=n; j++ )
                  for ( int k=1 ;k<=n; k++ )
                        c.mt[i][j]=(c.mt[i][j]+mt[i][k]*b.mt[k][j]%mod)%mod;
                return c;
        }
}f[50],ans;
void init( ll x )
{
        ll cnt=1;
        for ( int i=0; cnt<=x; i++,cnt<<=1 )
                f[i+1]=f[i]*f[i];
}
matrix power( ll b )
{
        matrix res; ll cnt=0;
        for ( ll i=1; i<=n; i++ )
                res.mt[i][i]=1;
        for ( ; b; b>>=1,cnt++ )
                if ( b&1 ) res=res*f[cnt];
        return res;
}
matrix split( ll x )
{
        if ( x==1 ) return f[0];
        matrix res=split( x>>1 );
        res=res+res*power(x>>1);
        if ( x&1 ) res=res+power(x);
        return res;
}
4——How many ways?(HDU2157)
题意
询问有向图上从 \(S\) 点恰好经过 \(k\) 个点到达 \(T\) 点的方案数对 1000 取模的余数。
思路
非常经典的一类题目,矩阵加速DP解决图上问题。这类题目相当于一种套路,在 \(NOI\ Online\) 和 \(NOI2020\) 里面都有出现,后面会讲到。
首先把给定的图转化为邻接矩阵(这注定了这类题目的点数不会很大,也是判断是不是用矩阵加速来做的一个标志)。
令 \(C=A\times A\) ,那么 \(C[i][j]=\sum A[i][k]\times A[k][j]\) ,实际上就等同于 \(i\) 到 \(j\) 恰好经过两条边的路径数量,\(k\) 步同理。
如何理解?一种方式是直接把矩阵乘法的转移看做是 \(Floyd\) 最短路,边权都是1;另一种方式是,考虑每次相乘的两个数,当且仅当都是 1,结果才会是1.放到整体去看就相当于 \((i,k),(k,j)\) 这两条边都是存在的,也就是经过两条边的路径。(相当于 Floyd 枚举中转点的思想)
代码
#include <bits/stdc++.h>
using namespace std;
const int N=110,mod=1000;
int n,m;
struct matrix
{
        int mt[N][N];
        matrix() { memset( mt,0,sizeof(mt) ); }
        matrix operator * ( const matrix &b )
        {
                matrix c;
                for ( int i=0; i<n; i++ )
                 for ( int j=0; j<n; j++ )
                  for ( int k=0; k<n; k++ )
                        c.mt[i][j]=(c.mt[i][j]+mt[i][k]*b.mt[k][j]%mod)%mod;
                return c;
        }
};
matrix power( matrix a,int b )
{
        matrix res;
        for ( int i=0; i<n; i++ )
         for ( int j=0; j<n; j++ )
                res.mt[i][j]=(i==j);
        for ( ; b; b>>=1,a=a*a )
                if ( b&1 ) res=res*a;
        return res;
}
int main()
{
        while ( cin>>n>>m && (n||m) )
        {
                matrix g;
                for ( int i=0,x,y; i<m; i++ )
                        scanf( "%d%d",&x,&y ),g.mt[x][y]=1;
                int q; scanf( "%d",&q );
                while ( q-- )
                {
                        int x,y,k; scanf( "%d%d%d",&x,&y,&k );
                        matrix res=power( g,k );
                        printf( "%d\n",res.mt[x][y] );
                }
        }
}
5——P2886 [USACO07NOV]Cow Relays G
题意
给出一张无向连通图,求 \(S\) 到 \(E\) 经过 \(k\) 条边的最短路。
思路
跟 4 一样,不过是把矩阵乘法里面的相乘换成了取min,多了一点 Floyd 的味道。注意矩阵初始化也得改改,毕竟是最短路。
然后还得离散化emmm。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=110;
int n,m,k,S,T;
struct matrix
{
        int mt[N][N];
        matrix() { memset( mt,0x3f,sizeof(mt) ); }
        matrix operator * ( const matrix &b )
        {
                matrix c;
                for ( int i=1; i<=n; i++ )
                 for ( int j=1; j<=n; j++ )
                  for ( int k=1; k<=n; k++ )
                        c.mt[i][j]=min(c.mt[i][j],mt[i][k]+b.mt[k][j]);
                return c;
        }
}g;
matrix power( matrix a,int b )
{
        matrix res=a; b--;
        for ( ; b; b>>=1,a=a*a )
                if ( b&1 ) res=res*a;
        return res;
}
int id[1010];
int main()
{
        scanf( "%d%d%d%d",&k,&m,&S,&T );
        memset( id,0,sizeof(id) );
        for ( int i=0,u,v,w; i<m; i++ )
        {
                scanf( "%d%d%d",&w,&u,&v );
                u=id[u] ? id[u] : id[u]=++n;
                v=id[v] ? id[v]  : id[v]=++n;
                g.mt[u][v]=g.mt[v][u]=min( g.mt[u][v],w );
        }
        S=id[S]; T=id[T];
        matrix ans=power( g,k );
        printf( "%d",ans.mt[S][T] );
}
中场休息
好的!那么这样我们就把 @[Xing_Ling] 神仙的作业写完了!bushi
现在,要不要再来点甜点呢——
6——[NOI Online3]提高组T3 魔法值
题目链接:Acwing1828 luogu
题意
\(n\) 座城市, \(m\) 条道路,任意两个城市之间之多一条道路。
在第 \(j\) 天,\(i\) 号城市的魔法值为 \(f_{i,j}\) 。给出所有的 \(f_{i,0}\) ,之后每一个城市的魔法值都是:
\]
其中 \(\oplus\) 表示异或,\(j\ge 1,v_1,...,v_k\) 是所有与 \(x\) 直接相连的城市。
给出 \(q\) 个询问,每次回答 \(a_i\) 天 1 号城市的魔法值。
思路
难度陡增 倍增+矩乘优化DP
看到题目里的 \(n\leq 100\) 和这个形式,结合上面两题,你就该想到这个做法(当然还要优化)。
仔细观察,发现最终要求的总是 1 号城市(原题面里的首都)的魔法值。考虑一个 \(f_{i,0}\) 如果产生了贡献,首先 1 一定和这个点有一条路径,而且根据异或的性质,异或偶数次跟没异或一样,那么只需要考虑长度为 \(a_i\) 的路径个数的奇偶性即可。于是用矩阵快速幂求 \(a_i\) 次方即可。
但是还不够——复杂度 \(O(n^3qloga)\) ,TLE了!怎么办呢?再想想那个特殊的要求——只需要知道 1 到其他的点的情况,其余都不用考虑。那么可以预处理邻接矩阵的 \(2^k\) 次方,倍增即可。复杂度 \(O(n^3\log a+qn^2\log a)\)
代码
/*
ai的范围是2^32,直接做肯定炸掉。考虑是2的幂次,可以用倍增解决。
首先,用dis[i,j,k]表示i到j长度为2^k的路径数量。矩乘计算dis。
对于一个询问,把ai二进制拆分。f[i,j]表示前j个二进制里的1,从1到i的方案数。
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=110,M=35;
ll f[N][M],road[N][N][M],a[N],a1[M];
int n,m,q;
int main()
{
	scanf( "%d%d%d",&n,&m,&q );
	for ( int i=1; i<=n; i++ )
		scanf( "%lld",&a[i] );
	for ( int i=1,ta,b; i<=m; i++ )
		scanf( "%d%d",&ta,&b ),road[ta][b][0]=road[b][ta][0]=1;
	for ( int i=1; i<M; i++ )
	 for ( int l=1; l<=n; l++ )
	  for ( int j=1; j<=n; j++ )
	   for ( int k=1; k<=n; k++ )
	   	road[j][k][i]+=road[j][l][i-1]*road[l][k][i-1];
	for ( int i=1; i<=q; i++ )
	{
		ll tot=0; ll tmp,ans=0; memset( f,0,sizeof(f) );
		scanf( "%lld",&tmp );
		for ( int j=M-1; j>=0; j-- )
			if ( (1ll<<j)<=tmp ) a1[++tot]=j,tmp-=1ll<<j;
		f[1][0]=1;
		for ( int j=1; j<=tot; j++ )
		 for ( int k=1; k<=n; k++ )
		  for ( int l=1; l<=n; l++ )
		  	f[k][j]+=f[l][j-1]*road[l][k][a1[j]];
		for ( int j=1; j<=n; j++ )
			f[j][tot]&=1;
		for ( int j=1; j<=n; j++ )
			if ( f[j][tot] ) ans^=a[j];
		printf( "%lld\n",ans );
	}
}
7——NOI2020 Day1T1 美食家
(别问我为什么不放 AcWing 的链接,因为我这题 LOJ 上过了但是 AcWing 上 TLE了……)
题意
有 \(n\) 个城市,\(1\sim n\) 编号,\(i\) 能提供 \(c_i\) 的愉悦值。有 \(m\) 条单向道路,道路 \(i\) 起始为 \(u_i,v_i\) ,花费 \(w_i\) 天(经过之后天数+=\(w_i\))。
计划进行为期 \(T\) 天的旅行,第 \(0\) 天从 \(1\) 出发,\(T\) 天后恰好回到 \(1\) 。每到达(包括起始点)一个城市会获得当地的愉悦值,且多次到达可以累加。中途不能停留。
有 \(k\) 个不同时间的节日,\(i\) 次在 \(t_i\) 天于 \(x_i\) 举办,如果当时在这个城市会额外得到 \(y_i\).
求愉悦值之和的最大值。
\(1\leq n\leq 50,n \leq m \leq 50,0 \leq k \leq 200,1\leq t_i \leq T \leq 10^9,1 \leq w_i \leq 5\)
思路
开始了.jpg (然而这已经是最后一题了!)
拆点,DP,矩乘,倍增优化
这类问题的解决方式(总结)
首先是描述:边有边权,问从 i 出发恰好过 k 条边到 j 的最长路
\(f[k,i,j]\) 表示上述意义,设邻接矩阵为 \(g\) ,得到转移:
\[f[k,i,j]=max_p\{f[k-1,i,p]+g_{p,j}\}
\]定义一个广义矩阵乘法:
\[C_{i,j}=max_k\{A_{i,k}+B_{k,j}\}
\]然后把 \(f[k]\) 看做一个矩阵,可以改写上式为:
\[f[k]=f[k-1]\times g
\]根据结合律(证明略),有
\[f[k]=f[0]\times g^k
\]于是得到了 \(O(n^3\log k)\) 的优良复杂度,结合倍增等方式能进一步优化。
回归题目。
首先,本题中是点权不是边权。那么把每个点的点权作为所有入边的边权,特判起始点点权。(\(f[0][1][1]=c_1\))
其次,每条边是当 \(w_i\) 条计时的,\(w_i\leq 5\) ,考虑拆点。边 \(e\) 拆分成若干点 \(e_i\) 表示在这条边上的 \(i\) 天。那么 \(e=(u,v,w)=> (u,e_1,0),...,(e_{w-2},e_{w-1},0),(e_{w-1},v,c_v)\)
最后,考虑如何处理 \(k\) 个美食节。在时间相邻的两个美食节之间,乘 \(g\) 转移,然后在美食节那天乘上一个举办城市入边边权加了 \(y\) 的转移矩阵即可。
但是这样子点数是 \(n+4m\) ,还是过不去。
考虑更换拆边为拆点,对于 \(u\to v=w\) 这个过程,看做 \(u\) 待了 \(w-1\) 天,第一天获得点权,在 \(w\) 天到达 \(v\) ,每个点 \(u\) 拆成 “待在 u 的第 i 天即可,点数 \(5n\).
朴素实现 \(O((5n)^3k\log t)\) ,由于最后只和 \(f[T,1,1]\) 有关, \(f[k]\) 保留第一行即可,再预处理 \(g^{2^k}\) 就可以通过本题。时间复杂度 \(O((5n)^3\log T+(5n)^2k\log T).\)
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=250+10,K=200+10;
int n,m,T,k,c[N],id[N][6],tot;
ll A[N];
struct node
{
        int time,x,y;
        bool operator < ( node tmp ) { return time<tmp.time; }
}t[K];
struct matrix
{
        ll s[N][N];
        matrix() { memset( s,0xc0,sizeof(s) ); }
        ll * operator [] ( int x ) { return s[x]; }
        matrix operator * ( matrix tmp )
        {
                matrix c;
                for ( int i=1; i<=tot; i++ )
                 for ( int k=1; k<=tot; k++ )
                 {
                         if ( s[i][k]<0 ) continue;
                         for ( int j=1; j<=tot; j++ )
                                c[i][j]=max( c[i][j],s[i][k]+tmp[k][j] );
                 }
                return c;
        }
}D[35];
void mul( matrix T )
{
        ll B[N];
        for ( int i=1; i<=tot; i++ )
                B[i]-=4e18;
        for ( int k=1; k<=tot; k++ )
        {
                if ( A[k]<0 ) continue;
                for ( int j=1; j<=tot; j++ )
                        B[j]=max( B[j],A[k]+T[k][j] );
        }
        for ( int i=1; i<=tot; i++ )
                A[i]=B[i];
}
int main()
{
        freopen( "delicacy.in","r",stdin ); freopen( "delicacy.out","w",stdout );
        scanf( "%d%d%d%d",&n,&m,&T,&k ); tot=n;
        for ( int i=1; i<=n; i++ )
                id[i][0]=i;
        for ( int i=1; i<=n; i++ )
                scanf( "%d",&c[i] );
        for ( int i=1,u,v,w; i<=m; i++ )
        {
                scanf( "%d%d%d",&u,&v,&w );
                for ( int j=1; j<w; j++ )
                        if ( !id[u][j] ) id[u][j]=++tot;
                for ( int j=1; j<w; j++ )
                        D[0][id[u][j-1]][id[u][j]]=0;
                D[0][id[u][w-1]][v]=c[v];
        }
        for ( int i=1,t1,t2,t3; i<=k; i++ )
                scanf( "%d%d%d",&t1,&t2,&t3 ),t[i]=(node){t1,t2,t3};
        sort( t+1,t+1+k ); t[0]=(node){0,0,0}; t[k+1]=(node){T,0,0};
        for ( int i=1; i<=30; i++ )
                D[i]=D[i-1]*D[i-1];
        for ( int i=2; i<=tot; i++ )
                A[i]=-4e18;
        A[1]=c[1];
        for ( int i=1; i<=k+1; i++ )
        {
                int del=t[i].time-t[i-1].time;
                for ( int j=30; ~j; j-- )
                        if ( del & (1<<j) ) mul( D[j] );
                A[t[i].x]+=t[i].y;
        }
        if ( A[1]<0 ) printf( "-1" );
        else printf( "%lld",A[1] );
        fclose( stdin ); fclose( stdout );
}
8——注意事项
- 矩阵初始化,尤其是多测;最短路和普通矩乘的初始化不一样。
 - 矩乘快速幂的 res 初始值,赋成单位矩阵或者递推式的初始答案矩阵
 - 矩乘和Floyd不一样的!!!Floyd的 k 循环一定要在外面(枚举中间点,不然你得跑很多遍),但是矩阵就是单纯乘法,没差。
 
Last——鸣谢
以上的题单(前面部分)均来源于:
的附录习题,另加了一些补充题目(NOI Online和NOI2020)。
要是想系统从0开始学习也推荐这篇博文。
矩阵乘法优化DP复习的更多相关文章
- 形态形成场(矩阵乘法优化dp)
		
形态形成场(矩阵乘法优化dp) 短信中将会涉及前\(k\)种大写字母,每个大写字母都有一个对应的替换式\(Si\),替换式中只会出现大写字母和数字,比如\(A→BB,B→CC0,C→123\),代表 ...
 - 斐波那契数列 矩阵乘法优化DP
		
斐波那契数列 矩阵乘法优化DP 求\(f(n) \%1000000007\),\(n\le 10^{18}\) 矩阵乘法:\(i\times k\)的矩阵\(A\)乘\(k\times j\)的矩 ...
 - [BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】
		
题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j ...
 - 矩阵乘法优化DP
		
本文讲一下一些基本的矩阵优化DP的方法技巧. 定义三个矩阵A,B,C,其中行和列分别为$m\times n,n \times p,m\times p$,(其中行是从上往下数的,列是从左往右数的) $C ...
 - 51nod 1583 犯罪计划——矩阵乘法优化dp
		
文泽想在埃及做案n次,并且想在最后不用得到惩罚.案件的被分成几种类型.比如说,案件A,当案件A被重复犯两次时,案件A将被认为不是犯罪案件,因此犯案人不用得到惩罚.也就是说,案件A被犯偶数次时,犯案人将 ...
 - 【bzoj2476】战场的数目  矩阵乘法优化dp
		
题目描述 (战场定义为对于最高的一列向两边都严格不增的“用积木搭成”的图形) 输入 输入文件最多包含25组测试数据,每个数据仅包含一行,有一个整数p(1<=p<=109),表示战场的图形周 ...
 - 【矩阵乘法优化dp】[Codeforces 621E] Wet Shark and Blocks
		
http://codeforces.com/problemset/problem/621/E E. Wet Shark and Blocks time limit per test 2 seconds ...
 - luoguP2768: 珍珠项链(矩阵乘法优化DP)
		
题意:有K种珍珠,每种N颗,求长度为1~N的项链,包含K种珍珠的项链种类数.N<=1e9, K<=30; 思路:矩阵快速幂,加个1累加前缀和即可. #include<bits/std ...
 - bzoj 4870: [Shoi2017]组合数问题 [矩阵乘法优化dp]
		
4870: [Shoi2017]组合数问题 题意:求 \[ \sum_{i=0}^{n-1} \binom{nk}{ik+r} \mod p \] \(n \le 10^9, 0\le r < ...
 
随机推荐
- IDEA与Eclipse创建struts项目
			
1.IDEA创建struts项目 这里再构建struts项目是选择jar包出问题了,可以重新配置 创建页面和action配置struts.xml 启动tomcat,浏览器中运行 具体参考: https ...
 - binary hacks读数笔记(nm命令)
			
nm命令(names):输出包含三个部分:1 符号值.默认显示十六进制,也可以指定: 2 符号类型.小写表示是本地符号,大写表示全局符号(external); 3 符号名称. 例如:nm Simple ...
 - linux文件cat/tac/more/less/head/tail/find/vimdiff
			
ls查看目录文件里的文件: [root@localhost test]# ls a aa b c -d选项查看目录文件自身信息: [root@localhost test]# ll -d drw ...
 - 使用Java将XSL和XML文件输出为HTML(XSL学习笔记二)
			
XSL 指扩展样式表语言(EXtensible Stylesheet Language),前面一篇博客介绍了使用XSL即可直接将XML输出为HTML片段被浏览器解析,但是这样在web应用中浏览器的解析 ...
 - wireshark实战应用(长期更新,工作随笔)
			
Wireshark检索语法 过滤IP地址 ip.addr eq 192.168.1.1 ip.addr == 192.168.1.1 //过滤源IP地址 ip.src eq 192.168.1.1 i ...
 - [原题复现+审计][网鼎杯 2018] WEB Fakebook(SSRF、反序列化、SQL注入)
			
简介 原题复现: 考察知识点:SSRF.反序列化.SQL注入 线上平台:https://buuoj.cn(北京联合大学公开的CTF平台) 榆林学院内可使用信安协会内部的CTF训练平台找到此题 过 ...
 - linux 用户组操作
			
1. 添加用户到...目录中useradd -M -s /目录 username 2. 添加用户属于多个组 usermod -G 本组(用户名),组1,组2... 用户名 3. mysql添加禁止登录 ...
 - vue中插值表达式中时间转换yyyy-MM-dd HH:mm:ss
			
vue插值表达式中将时间转换两种方式:一.定义方法 <div id="app">当前实时时间:{{dateFormat(date)}}</div> //时间 ...
 - FL studio系列教程(五):FL Studio20自带的效果器Fruity Delay3功能
			
作为音乐编曲常用软件之一的FL Studio20,在国内外都有着很多真爱粉,当然,在国内我们一般都叫它水果音乐制作软件,或者直接叫"水果".它有丰富的内置插件于音源,想要用好这些插 ...
 - nginx学习http_access_module模块
			
location ~ ^/1.html { root /opt/app/code; deny XXXXX; #这个ip不能访问1.html allow all; #其他的可以访问这个页面1.html ...