CQU 雷神之路(DP+矩陣加速)

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章