test20191020 往复

往复

Coldhac 做不出题了,他在长为 n 的走廊里走来走去。从位置 1 开始, 每次他会向前走长为 i ∈ [1, k] 的一步(不能超出走廊的范围),直至到达位 置 n。

在想出正解前,Coldhac 这样走了 m 次。你想知道他几乎经过了所有 位置的方案数模 109 + 7 的值。两种方案不同当且仅当总步数不同或某一步 的长度不同。

几乎的定义:至多有一个位置没有被直接到达。你可以参照样例解释 来更好地理解这句话。

对于所有测试点,满足 n ≤ 109 , m ≤ 16, k ≤ 3。

题解

此处只讨论 k = 3 的情况。

稍微压一下状态,fi,x,y,d 表示第 i 个格子有 x 个落点,第 i − 1 个格子 有 y 个落点(说明 i − 2 有 m − x − y 个落点),计算过的格子里有 d ∈ [0, 1] 个没有经过的方案数。

我是万万没想到还可以这样设状态。

枚举 i 和 i − 1 中分别选出多少跳到 i + 1 即可,如果此时 i + 1 没有 落点,我们令 d + +。

显然 i 一维可以矩乘,复杂度 O(3063 log n)。

CO int N=306;
int c[21][21];
typedef tuple<int,int,int> tup;
int K,id;
map<tup,int> H;
int cid(int x,int y,int d){
    if(K==2) y=0; // m-x-y
    return H[tup(x,y,d)];
}
int T[N][N],I[N][N],A[N],B[N];

int main(){
    freopen("iterate.in","r",stdin),freopen("iterate.out","w",stdout);
    int n=read<int>()-1,m=read<int>();
    if(read(K)==1) return puts("1"),0;
    for(int i=0;i<=m;++i){
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;++j) c[i][j]=add(c[i-1][j-1],c[i-1][j]);
    }
    for(int i=0;i<=m;++i)for(int j=0;K==3?i+j<=m:j<1;++j)
        for(int a=0;a<2;++a) H[tup(i,j,a)]=id++;
    for(int i=0;i<=m;++i)for(int j=0;K==3?i+j<=m:j<1;++j){
        int k=m-i-j;
        for(int a=0;a<2;++a)
            for(int x=0;x<=i;++x)for(int y=0;y<=j;++y){
                int b=x+y+k==0;
                if(a+b<=1) T[cid(i,j,a)][cid(x+y+k,i-x,a|b)]=mul(c[i][x],c[j][y]);
            }
    }
    A[cid(m,0,0)]=1;
    for(;n;n>>=1){
        if(n&1){
            for(int i=0;i<id;++i)for(int j=0;j<id;++j)
                B[j]=add(B[j],mul(A[i],T[i][j]));
            for(int i=0;i<id;++i) A[i]=B[i],B[i]=0;
        }
        for(int i=0;i<id;++i)for(int j=0;j<id;++j)
            for(int k=0;k<id;++k) I[i][j]=add(I[i][j],mul(T[i][k],T[k][j]));
        for(int i=0;i<id;++i)for(int j=0;j<id;++j) T[i][j]=I[i][j],I[i][j]=0;
    }
    printf("%d\n",add(A[cid(m,0,0)],A[cid(m,0,1)]));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/11709104.html