嘘~ 正在从服务器偷取页面 . . .

洛谷P2592 [ZJOI2008]生日聚会 题解


洛谷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


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录