题意:n条直线(a[i],b[i],c[i]) 表示在x=a[i]~b[i]内 运动的高度0<=y<=c[i],
a[i]=b[i-1],a[1]=0,a[n]<=k<=b[n],一个人有三个运动方向(x+1,y+1),(x+1,y),(x+1,y-1)
n<=100,c[i]<=15, a[i],b[i],k<=1e18 问从(0,0)->(k,0)的方法数?
设f[x][y] 从(0,0)到点(x,y)的方法数,
f[x][y]=f[x-1][y]+f[x-1][y-1]+f[x-1][y+1] 注意转移状态时要合法,x>=0&&0<=y<=c[i]
x很大并且x每次都只加1 明显用矩阵幂来优化 s(i,j)为纵坐标i->j的总方法数
构造h^x(i,j) 表示经过x步后 纵坐标从i变化到j的步数
根据每段的c[i],算出h^1,矩阵幂得到h^m,在更新s即可.O(n*h^3*logw)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=20;
const int M=2e2;
const ll mod=1e9+7;
ll s[N][N],h[N][N],t[N][N];//¾¹ýx²½ºó ×Ý×ø±ê´Ói±äµ½jµÄ·½·¨Êýh[i][j]
ll n,K,a[M],b[M],c[M];
void mul(ll a[][N],ll b[][N],ll c[][N])
{
ll t[N][N];
for(int i=1;i<=16;i++)
{
for(int j=1;j<=16;j++)
{
t[i][j]=0;
for(int k=1;k<=16;k++)
{
t[i][j]+=(a[i][k]*b[k][j])%mod;
t[i][j]%=mod;
}
}
}
for(int i=1;i<=16;i++)
for(int j=1;j<=16;j++)
c[i][j]=t[i][j]%mod;
}
void powmod(ll n)
{
memset(t,0,sizeof(t));
for(int i=1;i<=16;i++)
t[i][i]=1;
while(n)
{
if(n&1)
mul(h,t,t);
n>>=1;
mul(h,h,h);
}
}
int main()
{
while(cin>>n>>K)
{
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++)
scanf("%I64d%I64d%I64d",&a[i],&b[i],&c[i]),c[i]++;
memset(h,0,sizeof(h));
for(int i=1;i<=16;i++)//h^0
s[i][i]=h[i][i]=1;
for(int x=1;x<=n;x++)
{
memset(h,0,sizeof(h));
for(int i=1;i<=c[x];i++)
{
for(int j=1;j<=c[x];j++)
{
if(abs(i-j)<=1)
h[i][j]=1;
}
}
ll m=b[x]-a[x];
if(x==n)
m=K;
powmod(m);
K-=(b[x]-a[x]);
mul(t,s,s);
}
cout<<s[1][1]<<endl;
}
return 0;
}