[AtCoder Grand Contest 013] D: Pilling Up (agc013d)

原题链接
https://agc013.contest.atcoder.jp/tasks/agc013_d

Description

盒子中有N个积木,积木可能是黑色或白色的一种(状态未知)

现在要进行M次操作,每次操作拿出一个积木放在塔的顶端,然后将新的一黑一白两个积木放入盒子,再拿出一个积木放在塔的顶端

一开始是没有塔的,第一块放在平地上
求塔可能的颜色序列种数,对1e9+7取模
N,M<=3000

Solution

考虑DP
我们可能会从两个思路来想
设F[i][j]表示操作了i次,使用了j个黑色积木
或者设F[i][j]表示操作了i次,盒子中剩余j个黑色积木

事实上第一种是行不通的
第一种你可以算出黑色积木个数的上界来判断能否转移,但是由于初始的N个积木的状态没有确定,直接计算上界可能使得初始的N个积木一会作为黑色计算,一会作为白色计算,然而这是不合法的

考虑第二种
转移十分显然,每一次操作四种选法,j=0和j=n时判断一下

但是这样会算重复
假设n=2,m=1,F[0][2]和F[0][1]转移都能得到10这个序列(1表示黑)

我们发现,对于一条转移路径,只要黑球或者白球没有用完过,那么最后的都是会被计算到的

那么对于多种状态合法的,我们找一种卡住下界的,即强制使得转移过程中某个时候盒子中的黑球用完了(j=0)

状态改为F[i][j][0/1] 第三维表示是否盒子中的黑球用完过
复杂度O(4*n*m)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 3005
#define mo 1000000007
using namespace std;
int f[N][2*N][2],n,m;
int main()
{
    cin>>n>>m;
    f[0][0][1]=1;
    fo(i,1,n) f[0][i][0]=1;
    int ans=0;
    fo(i,1,m)
    {
        fo(j,0,n)
        {
            fo(p,0,1)
            {
                if(j) (f[i][j-1][p|(j==1)]+=f[i-1][j][p])%=mo,(f[i][j][p|(j==1)]+=f[i-1][j][p])%=mo;
                if(j<n) (f[i][j+1][p]+=f[i-1][j][p])%=mo,(f[i][j][p|(j==0)]+=f[i-1][j][p])%=mo;
            }
        }
    }
    fo(i,0,n) (ans+=f[m][i][1])%=mo;
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值