洛谷P2592 [ZJOI2008]生日聚会 题解
题目链接:P2592 [ZJOI2008]生日聚会
题意:
今天是 cxy 小朋友的生日,她邀请了许多朋友来参加她的生日party。 cxy 带着朋友们来到花园中,打算坐成一排玩游戏。为了游戏不至于无聊,就座的方案应满足如下条件:
对于任意连续的一段,男孩与女孩的数目之差不超过 $D$ 。
很快,小朋友便找到了一种方案坐了下来开始游戏。 cxy 的好朋友 q779 发现,这样的就座方案其实是很多的,所以大家很快就找到了一种,那么到底有多少种呢?热爱数学的 cxy 和她的朋友们开始思考这个问题……
假设参加party的人中共有 $n$ 个男孩与 $m$ 个女孩,你是否能解答 q779 和 cxy 的疑问呢?由于这个数目可能很多,他们只想知道这个数目除以 $12345678$ 的余数。
输入格式:
一行共 $3$ 个整数,分别为男孩数目 $n$ ,女孩数目 $m$ , 常数 $D$ 。
输出格式:
一行答案。
数据范围:
对于 $100\%$ 的数据, $n , m \le 150,~D \le 20$。
一开始我还以为是组合数,结果看看好像不太对(
显然是区间dp,但是状态如何设计?
这道题最难的就在于:「任意子段男女之差不超过 $D$ 」导致了朴素的区间dp即使记录了区间 $[i,j]$ 差值不超过 $k$ ,也无法合并。
于是我们从合并的角度观察加入一个人对整个当前区间的影响
方便起见,我们考虑在区间后面加入了一个人,
不难发现,这会影响到原区间的所有后缀。
于是考虑记录所有后缀的男女个数之差,显然只有极值会产生影响。
设 $f_{i,j,k,l}$ 表示放了 $i$ 个男生, $j$ 个女生,且当前拼出来的区间的所有后缀(包括空串 $\varnothing$ ),男生个数-女生个数的最大值为 $k$ ,女生个数-男生个数的最大值为 $l$ 的方案数。(好长啊,要不是看题解我怎么想得出…
下面用 $a\uparrow b$ 表示 a+=b
。
考虑在 $(i,j,k,l)$ 状态的后面加入一个男生,则
加入一个女生,则
注意一下转移的边界,就好了,这个不难,主要还是状态设计难
最后答案就是 $\sum\limits_{0 \le k,l \le D} f_{n,m,k,l}$
时间复杂度 $O(nmD^2)$
代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N ((int)(175))
#define K 25
int n,m,D,res=0,f[N][N][K][K];
const int mod = 12345678;
void add(int &x, int y){ (x+=y) >= mod ? x-=mod : 0;}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> m >> D;
f[0][0][0][0]=1;
for(int i=0; i<=n; i++)
for(int j=0; j<=m; j++)
for(int k=0; k<=i && k<=D; k++)
for(int l=0; l<=j && l<=D; l++)
if(f[i][j][k][l])
{
if(i < n && k < D)
add(f[i+1][j][k+1][max(0ll,l-1)], f[i][j][k][l]);
if(j < m && l < D)
add(f[i][j+1][max(0ll,k-1)][l+1], f[i][j][k][l]);
}
for(int k=0; k<=D; k++)
for(int l=0; l<=D; ++l)
add(res, f[n][m][k][l]);
cout << res << '\n';
return 0;
}
参考文献:
[1] https://yhx-12243.github.io/OI-transit/records/lydsy1037.html