AtCoder Grand Contest 013D: Piling Up 题解

来源:互联网 发布:涉密计算机检查软件 编辑:程序博客网 时间:2024/05/30 07:13

非常难的dp

容易想到状态dp[i][j]表示当前进行了i轮操作,第i轮操作结束后盒子里有j个红球的方案数,因为盒子里始终有N个球,所以蓝球的个数为N-j,于是可以转移

但这样有些状态会重复计数

例如:设N=2,则开始状态是Red=2,Blue=0时,可以获得“取出序列”Red,Blue,Red,Blue...

当开始状态是Red=1,Blue=1时,也可以获得“取出序列”Red,Blue,Red,Blue...

我们发现:同一个“取出序列”可以对应多种初始状态,所以有些“取出序列”会被重复计算

一个很好的比喻是,如果将这个“取出序列”在坐标系中化成一条折线,横坐标表示取到第几轮,纵坐标表示盒子里有几个红球

那么同样形状的折线对应的应该是同一种“取出序列”,然而由于折线的出发点不同,即刚开始红球的数量不确定,所以从每个点出发都会有这样一条折线

于是对于同样形状的曲线,我们考虑取其中最特殊的一条:初始状态中红球数最小的一条,即在坐标系中出发点最低的一条

最低的一条应该满足存在一个时刻,红球的数量为0,否则如果红球数量一直大于零,初始状态中的红球一定可以再减少

如下图,两条曲线代表同样的答案被计算多次,我们只计与x轴“相切”的那条


于是有新状态dp[i][j][0/1],第三维为0表示这个序列已经在之前(包括现在)的某时刻红球数为0,第三维为1反之亦然

我们有了正确的状态,但转移也非常难写

一方面要考虑到j=0,j=1,j=n的边界条件

另一方面,红红,红蓝,蓝红,蓝蓝四种转移方法,注意在红蓝和蓝红这两种转移方法中,在转移中间(即拿完红球还没补球时)红球数为0也算与x轴相切

#include <cstdio>#include <iostream>#include <cstring>#include <string>#include <algorithm>#include <cstdlib>#include <utility>#include <stack>#include <queue>#include <deque>#include <set>#include <map>#include <vector>#include <cmath>#define Pair pair<int,int>#define LOWBIT(x) x & (-x)#define LL long long#define mp make_pair#define pb push_back#define x first#define y secondusing namespace std;const int MOD=1e9+7;const int INF=0x7ffffff;const int magic=348;const double eps=1e-9;//ith step,j redLL dp[3048][3048][2];int n,m;inline LL mod(LL x){while (x>=MOD) x-=MOD;while (x<0) x+=MOD;return x;}int main (){int i,j,k;scanf("%d%d",&n,&m);dp[0][0][0]=1;for (i=1;i<=n;i++)dp[0][i][1]=1;LL ans=0;for (i=1;i<=m;i++)for (j=0;j<=n;j++){if (j==0){dp[i][j][0]=mod(dp[i-1][1][0]+dp[i-1][1][1]+dp[i-1][0][0]);if (i==m) ans=mod(ans+dp[i][j][0]);continue;}if (j==1){dp[i][j][0]=mod((j+1<=n?dp[i-1][j+1][0]:0)+(j==n?dp[i-1][j][0]:2*dp[i-1][j][0])+dp[i-1][j-1][0]+dp[i-1][j][1]);if (i==m) ans=mod(ans+dp[i][j][0]);dp[i][j][1]=mod((j+1<=n?dp[i-1][j+1][1]:0)+(j==n?0:dp[i-1][j][1]));continue;}dp[i][j][0]=mod((j+1<=n?dp[i-1][j+1][0]:0)+(j==n?dp[i-1][j][0]:2*dp[i-1][j][0])+dp[i-1][j-1][0]);dp[i][j][1]=mod((j+1<=n?dp[i-1][j+1][1]:0)+(j==n?dp[i-1][j][1]:2*dp[i-1][j][1])+dp[i-1][j-1][1]);if (i==m) ans=mod(ans+dp[i][j][0]);}printf("%I64d\n",ans);return 0;}


原创粉丝点击