[agc013d]Piling Up

来源:互联网 发布:网页怎么连接数据库 编辑:程序博客网 时间:2024/05/29 12:51

前言

一开始写了个naive的dp发现方案数跟初始放球无关只和颜色序列有关所以我算重了。
然后想到了强制到达0,但发现会算漏(因为0会卡下界)。
然后想到加上强制到达1就不卡下界了,但发现还是会算重(如果这个方案没有执行某种转移)。
最终弄出来才觉得这种“简单dp”题的精髓还是挺秒的。

题意

在箱子里放n个球,有黑白两色。
执行m轮操作:
抓箱子里一个球堆在塔顶。
往箱子里放入一个黑球和一个白球。
再抓箱子里的一个球堆在塔顶。
求塔的方案数(按颜色看)

DP

箱子里的球永远是n个。
假设当前有x个黑球,用0表示黑1表示白。
如果塔接下来两个是00,那么x要大于0,然后x-1。
如果塔接下来两个是01,那么x要大于0,然后x不变。
如果塔接下来两个是10,那么x要小于n,然后x不变。
如果塔接下来两个是11,那么x要小于n,然后x+1。
x初始在[0,n]内,你构造的颜色序列需要使x存在。
我们可以发现,对于一种方案,如果初始x唯一,x一定某时刻为0。
而假如初始x不唯一,对于最小的那个初始x,一定存在某时刻其为0。
如果只统计这个最小的初始x的方案显然不对,因为x=0时会卡下界,无法进行第二个转移。
这个初始x不唯一,那么对于第二小的那个初始x,一定存在某时刻为1不存在一个时刻为0。
相对于最小的初始x,这个初始x可能会卡上界,但是不会卡下界。
我们希望不重复不遗漏的统计所有答案。
因此可以dp这个颜色序列,并强制存在某时刻x=0或某时刻x=1并且执行了第二个转移。这样显然不重不漏。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=3000+10,mo=1000000007;int f[maxn][maxn][2],g[maxn][maxn][2][2];int i,j,k,l,t,n,m,ans;int main(){    scanf("%d%d",&n,&m);    f[0][0][1]=1;    fo(i,1,n) f[0][i][0]=1;    fo(i,0,m-1)        fo(j,0,n)            fo(k,0,1){                if (j) (f[i+1][j-1][k|(j-1==0)]+=f[i][j][k])%=mo;                if (j) (f[i+1][j][k|(j==1)]+=f[i][j][k])%=mo;                if (n-j) (f[i+1][j][k]+=f[i][j][k])%=mo;                if (n-j) (f[i+1][j+1][k]+=f[i][j][k])%=mo;            }    fo(i,0,n) (ans+=f[m][i][1])%=mo;    printf("%d\n",ans);}