【BZOJ】1037: [ZJOI2008]生日聚会Party(递推+特殊的技巧)
http://www.lydsy.com/JudgeOnline/problem.php?id=1037
看来自己越来越弱了。。。
这些计数题设计的状态都很巧妙,,自己智商太低QAQ
和矩阵dp做的那题差不多,都是枚举当前点的情况然后开了一些维来维护从当前点向前延伸的一些状态。
设d[i,j,x,y]表示前i个男的前j个女的,x表示从当前点向前延伸得到的最大的男减女的差,y表示从当前点向前延伸得到的最大的女减男的差。
则d[i+1,j,x+1,max(y-1,0)]+=d[i,j,x,y],d[i,j+1,x,max(y-1,0)]+=d[i,j,x,y]
这样设计状态能不重不漏的统计且能转移是因为,转移的点是i+1,j+1,而x和y设计的状态只用考虑当前的和i+1或j+1这个点
还有个地方我竟然考虑了很久?就是max那里,会不会不正确?但是想想就应该知道,假如变成了-1,那么总有办法从前边去掉一个使得变成0,而我们所设计的状态要保证任意一段最大,所以存在了最大的段,即0
最后答案就是sum{d[n,m,x,y], 0<=x,y<=k}
#include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <algorithm> #include <queue> #include <set> #include <map> using namespace std; typedef long long ll; #define pii pair<int, int> #define mkpii make_pair<int, int> #define pdi pair<double, int> #define mkpdi make_pair<double, int> #define pli pair<ll, int> #define mkpli make_pair<ll, int> #define rep(i, n) for(int i=0; i<(n); ++i) #define for1(i,a,n) for(int i=(a);i<=(n);++i) #define for2(i,a,n) for(int i=(a);i<(n);++i) #define for3(i,a,n) for(int i=(a);i>=(n);--i) #define for4(i,a,n) for(int i=(a);i>(n);--i) #define CC(i,a) memset(i,a,sizeof(i)) #define read(a) a=getint() #define print(a) printf("%d", a) #define dbg(x) cout << (#x) << " = " << (x) << endl #define error(x) (!(x)?puts("error"):0) #define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; } #define printarr1(a, b) for1(_, 1, b) cout << a[_] << '\t'; cout << endl inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; } inline const int max(const int &a, const int &b) { return a>b?a:b; } inline const int min(const int &a, const int &b) { return a<b?a:b; } const int N=155, MD=12345678; int d[N][N][25][25], n, m, k; inline void add(int &a, const int &b) { a=(a+b)%MD; } int main() { read(n); read(m); read(k); d[0][0][0][0]=1; for1(x, 0, n) for1(y, 0, m) for1(k1, 0, k) for1(k2, 0, k) { if(k1+1<=k) add(d[x+1][y][k1+1][max(k2-1, 0)], d[x][y][k1][k2]); if(k2+1<=k) add(d[x][y+1][max(k1-1, 0)][k2+1], d[x][y][k1][k2]); } int ans=0; for1(x, 0, k) for1(y, 0, k) add(ans, d[n][m][x][y]); print(ans); return 0; }
Description
今天是hidadz小朋友的生日,她邀请了许多朋友来参加她的生日party。 hidadz带着朋友们来到花园中,打算坐成一排玩游戏。为了游戏不至于无聊,就座的方案应满足如下条件:对于任意连续的一段,男孩与女孩的数目之差不超过k。很快,小朋友便找到了一种方案坐了下来开始游戏。hidadz的好朋友Susie发现,这样的就座方案其实是很多的,所以大家很快就找到了一种,那么到底有多少种呢?热爱数学的hidadz和她的朋友们开始思考这个问题…… 假设参加party的人中共有n个男孩与m个女孩,你是否能解答Susie和hidadz的疑问呢?由于这个数目可能很多,他们只想知道这个数目除以12345678的余数。
Input
仅包含一行共3个整数,分别为男孩数目n, 女孩数目m, 常数k。
Output
应包含一行,为题中要求的答案。
Sample Input
Sample Output
HINT
对于100%的数据, n , m ≤ 150,k ≤ 20。