LUOGU P1357 花园

 

题目描述

小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(2<=N<=10^15)。他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻M(2<=M<=5,M<=N)个花圃中有不超过K(1<=K

解题思路

首先考虑80分做法:可以用dp,dp[i][S] 表示到了第i位,这m位的状态为S,这样的转移比较好写。再考虑满分做法,看到数据范围应该能猜出是矩阵乘法加速dp,因为局面中的任何一个状态都在 1<

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;
const int mod = 1e9+7;
typedef long long LL;

LL n,ans;
int m,K;
bool ok[1<<6];

struct Mat{
    LL x[1<<6][1<<6];
    Mat(){memset(x,0,sizeof(x));}
    Mat operator *(const Mat &h){
        Mat c;
        for(register int i=0;i<1<<m;i++)
            for(register int j=0;j<1<<m;j++)
                for(register int k=0;k<1<<m;k++)
                    c.x[i][j]=(c.x[i][j]+x[i][k]*h.x[k][j])%mod;
        return c;
    }
}pre,a,b;

inline void fast_pow(Mat A,LL t){
    for(;t;t>>=1){
        if(t&1) b=b*A;
        A=A*A;
    }
}

int main(){
    scanf("%lld%d%d",&n,&m,&K);
    for(register int i=0;i<1<<m;i++){
        int tmp=i,cnt=0;
        while(tmp) {
            if(tmp&1) cnt++;
            tmp>>=1;
        }
        if(cnt<=K) {
            ok[i]=1;
            a.x[i>>1][i]=1;
            if((i&1)==1 || cnt<K) a.x[(i>>1)|(1<<m-1)][i]=1;
        }
    }
    for(register int i=0;i<1<<m;i++) b.x[i][i]=1;
    fast_pow(a,n);
    for(register int i=0;i<1<<m;i++)
        if(ok[i]) ans=(ans+b.x[i][i])%mod;
    cout<<ans<<endl;
    return 0;
}
posted @ 2018-08-19 21:28  Monster_Qi  阅读(122)  评论(0编辑  收藏  举报