您的位置:首页 > 其它

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

2018-03-04 16:57 501 查看
原题链接

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
[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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: