组合数 dp T2 todo #10230. 「一本通 6.6 练习 1」牡牛和牝牛 - 题目 - LibreOJ (loj.ac) https://vjudge.csgrandeur.cn/problem/LibreOJ-10230

题目描述

原题来自:USACO 2009 Feb. Silver

牡 mǔ,畜父也。牝 pìn,畜母也。 ——《说文解字》

约翰要带  只牛去参加集会里的展示活动,这些牛可以是牡牛,也可以是牝牛。牛们要站成一排,但是牡牛是好斗的,为了避免牡牛闹出乱子,约翰决定任意两只牡牛之间至少要有  只牝牛。

请计算一共有多少种排队的方法,所有牡牛可以看成是相同的,所有牝牛也一样,答案对  取模。

输入格式

一行,输入两个整数  和 

输出格式

一个整数,表示排队的方法数。

样例

输入

4 2

输出

6

6 种方法分别是:牝牝牝牝,牡牝牝牝,牝牡牝牝,牝牝牡牝,牝牝牝牡,牡牝牝牡。 (母母母母,公母母母,母公母母,母母公母,母母母公,公母母公)

数据范围与提示

对于全部数据,

思路

让1为牡牛, 0为牝牛, 那么问题就转化为找满足的01串方案数。

用类似递推DP的方法: 表示所有长度为 , 且以 1 结尾的字符串数量。

若当前为1, 两个1之间需要隔 个0, 考虑一下怎么转移: , 且 , 把所有情况都累加起来就行。

此时若 时, 我们仍然需要把 加上, 首先找特殊值也可以看出来, 一定最小是1, 故 且需要把 加上, 其次也可以从定义出发, 在第 个位置上以 1 结尾, 前面不存在1的话也是成立的。

因此可以直接DP求出 数组。 但这里要求的是满足方案数, 要求没有重复的串, 而我们按状态定义划分后, 可以分成0~n长度的以1为结尾的串, 这种划分是不重不漏的, 故直接进行累加 就可以得到最终结果。

总时间复杂度为 而这里 , 需要进行优化。可以发现我们每次算 时都是算某一个前缀的和, 我们可以直接将之前算过的前缀和保存下来, 优化一层循环, , 且最终答案也就是

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int MOD = 5000011, N = 1e5 + 10;
int f[N], s[N];
int n, k;
 
int main()
{
    cin >> n >> k;
    f[0] = s[0] = 1;
    for (int i = 0; i <= n; i++)
    {
        f[i] = s[max(i - k - 1, 0)];
        s[i] = (s[i - 1] + f[i]) % MOD;
    }
 
    cout << s[n] << endl;
 
    return 0;
}