Time Limit: 10000 MSMemory Limit: 32768 K
雷神之路
描述
zzblack成功通关SAO,现在,等待他的将是雷神之路。雷神之路
是在x轴上的一条路。雷神在路的尽头放了无尽的宝藏。一开始,
zzblack在初始点0,每次能走一步,两步或者三步,路的尽头的点
坐标为n。雷神为了增大游戏的难度,在路上放置了m个地雷(不能
踩上去),每个地雷的坐标为,然而,zzblack破解了这个游戏,
因此他知道每个地雷的坐标。他不屑于知道能否拿到宝藏,他只想
知道有多少种方法能拿到宝藏。
输入
第一行输入一个T表示有T组样例
接下来块
每块第一行
接下来一行描述个地雷的位置
输出
输出方案数
样例输入
2
4 1
3
6 2
1 2
样例输出
3
4
思路:
还是从朴素开始:
f[i]定义为 f[i] 到达坐标i的方案数
朴素肯定是O(N)的递推:
f[i]=f[i-1]+f[i-2]+f[i-3] (如果有地雷的话,则对应f为0)
但是 N≤1e18 ,又是递推 所以很容易想到矩阵加速。
矩阵加速是在没有地雷的情况下可以直接算出f[i]
那么困难就变成了如何去掉地雷的影响
换一种方式说 地雷会产生怎样的影响
其实第i个地雷对后面的方案数造成的影响中
只有第i+1个地雷前的两个f[i-1] f[i-2]是有用的
所以我们只用算第i个地雷前面的两个个f[i-1]f[i-2]就好了。
然后f[i]=0 (是地雷嘛) 于是 f[i],f[i-1],f[i-2] 就是可以构成一个新的矩阵快速幂的起点。然后去算一下个地雷,直到算到n
复杂度 kO(m) k是矩阵快速幂的复杂度,log2(1e18)*27=60*27的样子吧= =。。
制杖思考:
一开始想了个m^2的算法用f[i]去把后面所有的地雷的方案刷一遍。。
T有500组 肯定过不了辣。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define mem(array) memset(array,0,sizeof array)
const int MOD=1e9+7;
int T,n,m;
int a[505];
long long f[505];
long long js(int x){
if(x==0) return 0;
if(x==1) return 1;
x--;
long long D[3][3];
long long C[3][3]={1,0,0,0,1,0,0,0,1};
long long A[3][3]={1,1,0,1,0,1,1,0,0};
while(x){
if(x & 1) {
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
{
D[i][j]=0;
for(int k=0;k<3;k++) //C[i][k]=A[k][j];
D[i][j]=(D[i][j]+C[i][k]*A[k][j]) % MOD;
}
for(int i=0;i<3;i++)
for(int j=0;j<3;j++) C[i][j]=D[i][j];
}
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
{
D[i][j]=0;
for(int k=0;k<3;k++)
D[i][j]=(D[i][j]+A[i][k]*A[k][j]) %MOD;
}
for(int i=0;i<3;i++)
for(int j=0;j<3;j++) A[i][j]=D[i][j];
x=x>>1;
}
return (C[0][0]+C[1][0]) % MOD;
}
int main(){
freopen("in.txt","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&m);
mem(f);mem(a);
//cout<<js(3)<<endl;
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
sort(a+1,a+1+m);
for(int i=1;i<=m;i++)
{
f[i]+=js(a[i]);
for(int j=i+1;j<=m;j++)
{
f[j]-=f[i]*js(a[j]-a[i]) % MOD;
}
//cout<<f[i]<<endl;
}
long long ans=js(n);
for(int i=1;i<=m;i++)
if(n>a[i])
ans=(ans-f[i]*js(n-a[i])) % MOD;
//for(int i=1;i<=n;i++) cout<<f[i]<<
if(ans>0 && a[m]!=n)printf("%lld\n",ans);
else cout<<0<<endl;
}
return 0;
}