洛谷1357 花园

题解 算法-搜索 算法-状态压缩 数学-快速幂 数学-矩阵 省选/NOI-
题目链接 编辑文章

题意

要求构造一个长度为 $n(\le 10^{15})$ 的 $0/1$ 环形序列,满足任意连续 $m(2\le m \le 5)$ 个数的和不超过 $k$ 。求方案数。

题解

$m$ 比较小,可以状压。可以发现答案其实就是把一个状态往后推 $n$ 次,最后回到自己的方案数。

两个状态之间的转移关系 $s[i][j]$ 可以提前预处理出来,然后把 $s$ 看成一个矩阵,合法状态 $i$ 对答案的贡献就是 $s^n[i][i]$ 。

注意:因为矩阵下标从 $0$ 开始,所以也要相应的改构造函数。

#include<bits/stdc++.h>
#define ll long long
#define ha 1000000007

using namespace std;

inline ll read()
{
    char ch=getchar(); ll f=1,x=0;
    while (ch<'0' || ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
    while (ch>='0' && ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
    return f*x;
}

struct matrix {
    ll n,s[32][32];
    matrix (ll len=-1) {
        n=len;
        memset(s,0,sizeof(s));
        for (ll i=0;i<=n;i++) s[i][i]=1;
    }
    matrix operator * (const matrix &x) const {
        matrix y; y.n=n;
        for (ll i=0;i<=n;i++)
            for (ll j=0;j<=n;j++)
                for (ll k=0;k<=n;k++)
                    y.s[i][j]=(y.s[i][j]+s[i][k]*x.s[k][j]%ha)%ha;
        return y;
    }
} x;
bool ok[32];
ll n,m,k,M;

inline void work(ll cnt,ll cur)
{
    ok[cur]=1;
    ll lst=cur>>1; //上一个状态
    x.s[lst][cur]=1;
    if (cnt==k && !(cur&1)) return; //不能再放1了
    x.s[lst|(1<<(m-1))][cur]=1;
}

void dfs(ll x,ll cnt,ll cur) //用dfs枚举当前状态
{
    if (x==m+1) return work(cnt,cur);
    dfs(x+1,cnt,cur);
    if (cnt<k) dfs(x+1,cnt+1,cur|(1<<(x-1)));
}

inline matrix qpow(ll y)
{
    matrix ans(M-1);
    while (y)
    {
        if (y&1) ans=ans*x;
        y>>=1;
        x=x*x;
    }
    return ans;
}

signed main()
{
    n=read(); m=read(); k=read();
    M=1<<m; x.n=M-1;
    dfs(1,0,0);
    matrix res=qpow(n);
    ll ans=0;
    for (ll i=0;i<M;i++) ans=(ans+res.s[i][i])%ha;
    return !printf("%lld",ans);
}

新评论

称呼不能为空
邮箱格式不合法
网站格式不合法
内容不能为空