题意 :你从起点(0,0)走到终点(k,0),其中你每次只可以走右边,右上,右下三个位置,并且有一个附加条件,对于对应的区间,y有个上限。
思路: 看这题一开始有点像搜索,算算复杂度和实现方法,很麻烦。 因为每次你所能到达位置的方案数和前面能到这个点的方案数有关,因此可以用dp做。 状态转移方程为 dp[i][j]=dp[i-1][j]+dp[i-1][j+1]+dp[i-1][j-1],状态转移方程很容易,复杂度却达到O(k) (k<=1e18) 明显超时。因此对于这题,y很小,k很大,因此可以用矩阵快速幂来来优化dp过程,复杂度自最差变为O(n*(15)^3log(k))≈2e7 ;一个log , 把k降低到只要60左右, 太强了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll MOD=1e9+7;
typedef struct
{
ll mat[20][20];
}matrix;
// 矩阵乘法
matrix matrix_mul(matrix a,matrix b,ll y)
{
matrix c;
memset(c.mat,0,sizeof(c.mat));
for(ll i=0;i<=y;i++)
{
for(ll j=0;j<=y;j++)
{
for(ll k=0;k<=y;k++)
{
c.mat[i][j]+=(a.mat[i][k]%MOD*b.mat[k][j]%MOD)%MOD;
c.mat[i][j]%=MOD;
}
}
}
return c;
}
// 快速幂
matrix matrix_quick_power(matrix a,ll k ,ll y)
{
matrix b;
memset(b.mat,0,sizeof(b.mat));
for(int i=0;i<=15;i++)
b.mat[i][i]=1;
while(k)
{
if(k%2==1) b=matrix_mul(b,a,y);
k=k/2;
a=matrix_mul(a,a,y);
}
return b;
}
matrix a,b,c;
int main(void)
{
ll n,k;
cin >> n >>k;
memset(a.mat,0,sizeof(a.mat));
for(int i=0;i<=15;i++)
for(int j=i-1;j<=i-1+2;j++)
if(j>=0 && j<=15)
a.mat[i][j]=1;
/*for(int i=0;i<=15;i++) { for(int j=0;j<=15;j++) printf("%d ",a.mat[i][j]); puts(""); }*/
c.mat[0][0]=1;
for(int i=1;i<=n;i++)
{
ll l,r,y;
scanf("%I64d%I64d%I64d",&l,&r,&y);
if(r>k) r=k;
for(int i=y+1;i<=18;i++) c.mat[0][i]=0; // 一定要这么处理,否则对于y为 5 2 6 中,5的结果会对6造成影响。
b=matrix_quick_power(a,r-l,y);
c=matrix_mul(b,c,y);
}
printf("%I64d\n",c.mat[0][0]%MOD);
}
1.学习到了矩阵快速幂,其实和数的快速幂原理几乎一样。
矩阵快速幂适合作用于有递推方程的,并且dp[i] 的i范围比较小的情况,对于这道题,i最多只有15,k=1e18,dp次数很多。所以很明显用dp+矩阵快速幂了
2.第一次做了E题,其实也没有那么难啊~
3.今天被猫抓了.,.. 打了两针疫苗