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;
}
posted @ 2018-06-07 23:05 SCaryon 阅读(...) 评论(...) 编辑 收藏