[DP记录]AT2370 [AGC013D] Piling Up

command_block

2021-04-29 07:40:15

Personal

**题意** : 一开始袋子中有 $n$ 个黑白球,但不知道黑白色各有多少。 进行 $m$ 次操作,每次先拿出一个球,再放入黑白球各一个,再拿出一个球。 将拿出的球按顺序排列,求形成的颜色序列有多少种。答案对 $10^9+7$ 取模。 $n,m\leq 3000$ ,时限$\texttt{2s}$。 ------------ 不难发现,操作后袋子中的总球数不变。 先考虑给定初始时有 $x$ 个黑球的情况。 $dp[i][j]$ 表示进行了 $i$ 次操作,目前袋子中有 $j$ 个黑球的方案数。 边界为 $dp[0][x]=1$。 操作时,分三种情况讨论 : - 取出黑黑 : 黑球个数减少一个, $dp[i+1][j-1]{\texttt{ += }}dp[i][j]$ - 取出白白 : 黑球个数增加一个, $dp[i+1][j+1]{\texttt{ += }}dp[i][j]$ - 取出黑白或白黑 : 黑球个数不变, $dp[i+1][j]{\texttt{ += }}2dp[i][j]$ 特殊地,当 $j=0$ 时,不能取黑白,当 $j=n$ 时,不能取白黑。 现在考虑初始时黑球数目不定的情况。似乎只需要将 $dp[0][0\sim n]$ 都置为 $1$ 就好了? 这样会导致重复计数。我们将一个操作方案中袋中黑球的变化情况画成折线,平移后能重合的折线产生的颜色序列是相同的,但却被统计了多次。 于是,我们只统计接触过 $x$ 轴的折线(即中途黑球个数曾为 $0$ 的方案),这样就不会重复了。 多开一维 $0/1$ 记录这个。 复杂度 $O(n^2)$。 ```cpp #include<algorithm> #include<cstdio> #define MaxN 3050 using namespace std; const int mod=1000000007; int n,m,dp[MaxN][MaxN][2]; int main() { scanf("%d%d",&n,&m); dp[0][0][1]=1; for (int j=1;j<=n;j++) dp[0][j][0]=1; for (int i=0;i<m;i++) for (int j=0;j<=n;j++){ if (j>0){ if (j==1)dp[i+1][j-1][1]=(dp[i+1][j-1][1]+dp[i][j][0])%mod; else dp[i+1][j-1][0]=(dp[i+1][j-1][0]+dp[i][j][0])%mod; dp[i+1][j-1][1]=(dp[i+1][j-1][1]+dp[i][j][1])%mod; if (j==1)dp[i+1][j][1]=(dp[i+1][j][1]+dp[i][j][0])%mod; else dp[i+1][j][0]=(dp[i+1][j][0]+dp[i][j][0])%mod; dp[i+1][j][1]=(dp[i+1][j][1]+dp[i][j][1])%mod; } if (j<n){ dp[i+1][j+1][0]=(dp[i+1][j+1][0]+dp[i][j][0])%mod; dp[i+1][j+1][1]=(dp[i+1][j+1][1]+dp[i][j][1])%mod; dp[i+1][j][0]=(dp[i+1][j][0]+dp[i][j][0])%mod; dp[i+1][j][1]=(dp[i+1][j][1]+dp[i][j][1])%mod; } } int ans=0; for (int j=0;j<=n;j++) ans=(ans+dp[m][j][1])%mod; printf("%d",ans); return 0; } ```