bzoj5190: [Usaco2018 Jan]Stamp Painting (dp)

本文探讨了一种利用动态规划解决印章排列计数问题的方法。给定不同颜色的印章和一张纸条,目标是计算所有可能的印章排列组合数量。文章详细介绍了如何通过递推公式计算合法排列,并给出了具体的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem

M M M 种长为 K K K 的印章(每种颜色均不同),给长为 N N N 的纸条盖章。
问最后会得到多少种不同的状态。

Solution

反过来想,答案就是总数减去不合法情况。
总数也就是每个块随便选颜色,即 m n m^n mn
而每个印章长度为 K K K ,因此如果最长的一段相同颜色的长度小于 K K K ,那么就说明此情况不合法。

那么我们现在的认为就是求不合法的情况。
定义 f [ i ] f[i] f[i] 表示到 i i i 位置不合法的情况。
f [ i ] = ∑ f [ j ] ∗ ( m − 1 ) i − k + 1 &lt; = j &lt; i f[i]=\sum f[j]*(m-1)\quad i-k+1&lt;=j&lt;i f[i]=f[j](m1)ik+1<=j<i
(表示从 j + 1 j+1 j+1 i i i 设置成与 j j j 位置颜色不同的一种颜色,此时由定义前 j j j 个中是没有大于等于 k k k 个连续的颜色相同的,且 j + 1 j+1 j+1 i i i 长度小于 k k k

维护个前缀和就好啦。

Code

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 1000010
#define mod 1000000007
#define ll long long
int n,m,k;
ll f[N],sum[N];
inline ll pow(ll x,int y){
    ll t=1;
    while(y){
        if(y&1) t=t*x%mod;
        x=x*x%mod;y>>=1;
    }return t;
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    f[0]=1;sum[0]=1;
    for(int i=1;i<k;i++) f[i]=f[i-1]*m%mod,sum[i]=(sum[i-1]+f[i])%mod;
    for(int i=k;i<=n;i++){
        f[i]=(sum[i-1]-sum[i-k])%mod*(m-1)%mod;
        sum[i]=sum[i-1]+f[i];
    }
    printf("%lld\n",(pow(m,n)-f[n]+mod)%mod);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值