ZOJ 3747 Attack on Titans

题目大意

一共有N个人,编号为 (1...N)

题目要求给每一个人都要有一个属性,分别为 (R,P,G)

并且对于这些属性的人有一定的要求:

1.属性为 (G) 的人至少连续有 (m)

2.最多有连续 (k) 个为 (R) 属性的人

问符合这种要求的排列有多少个

大致思路:

这个题比较麻烦的是有两个条件,一个最多、一个最少。所以我们需要尽量把这两个条件变成一个条件。

我们可以求出 (G) 最多长度为 (n)(R) 最多长度为 (k) 时的情况数,减去

(G) 最多长度为 (m-1)(R) 最多长度为 (k) 时的情况数。

相减结果就是答案。

所以我们考虑一个 (DP[i][0...2]) 表示第 (i) 个人为 (R,P,G) 时的情况。

下面以 (G) 来考虑 (DP) 式子的转移公式:

(G) 最多长度为 (u) 时:

(i<=u) 很明显,不存在非法的情况,所以状态直接可以进行转移:(dp[i][0] = dp[i-1][0]+dp[i-1][1]+dp[i-1][2])

(下面为了方便将 (sum= dp[i-1][0]+dp[i-1][1]+dp[i-1][2])

(i = u+1) 时,存在一种情况非法:即从 (1...i) 都是 (G) 。所以:

$dp[i][0] = sum-1 $

(i>u+1) 时。存在非法情况为:(G) 长度为 (u+1)

这个非法情况的前提是:第 (i-u) 位是 (G)

(i-u) 位是 (G) 的前提是: 第 (i-u-1) 位不是 (G)

(i-u-1) 位不是 (G) ,那么肯定就是其他的两种情况。所以我们得出式子:

$ dp[i][0] = sum - dp[i-u-1][1]-dp[i-u-1][2] $

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
const int mod=1e9+7;
ll dp[maxn][3];
int n,m,k;
inline ll slove(int u,int k)
{
    dp[0][0]=1;
    dp[0][2]=dp[0][1]=0;//什么都不放的时候,整体方案数为1
    for(int i=1;i<=n;++i){
        ll sum=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]+mod)%mod;
        dp[i][2]=sum;
        if(i<=u)
            dp[i][0]=sum;
        else if(i==u+1)
            dp[i][0]=(sum-1+mod)%mod;
        else
            dp[i][0]=(sum-dp[i-u-1][1]-dp[i-u-1][2]+mod)%mod;
        if(i<=k)
            dp[i][1]=sum;
        else if(i==k+1)
            dp[i][1]=(sum-1+mod)%mod;
        else
            dp[i][1]=(sum-dp[i-k-1][0]-dp[i-k-1][2]+mod)%mod;
        //cout<<dp[i][0]<<" "<<dp[i][1]<<" "<<dp[i][2]<<endl;
    }
    return (dp[n][0]+dp[n][1]+dp[n][2]+mod)%mod;
}
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>m>>k)
    {
        cout<<((slove(n,k)-slove(m-1,k))%mod+mod)%mod<<endl;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/SCaryon/p/9153263.html