【HDU5894 2016 ACM ICPC Asia Regional Shenyang Online C】【组合数 隔板法】hannnnah_j’s Biological Test n个不同座位成

hannnnah_j’s Biological Test

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 866    Accepted Submission(s): 296


Problem Description
hannnnah_j is a teacher in WL High school who teaches biology.

One day, she wants to test m students, thus she arranges n different seats around a round table.

In order to prevent cheating, she thinks that there should be at least k empty seats between every two students.

hannnnah_j is poor at math, and she wants to know the sum of the solutions.So she turns to you for help.Can you help her? The answer maybe large, and you need to mod 1e9+7.
 

Input
First line is an integer T(T≤1000).
The next T lines were given n, m, k, respectively.
0 < m < n < 1e6, 0 < k < 1000
 

Output
For each test case the output is only one integer number ans in a line.
 

Sample Input
  
  
2 4 2 6 5 2 1
 

Sample Output
  
  
0 5
 

Source

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 2e6, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m, k;
LL fac[N + N];
LL inv[N + N];
LL C(int n, int m)
{
	return fac[n] * inv[m] % Z*inv[n - m] % Z;
}
LL fillbox(int n, int m)//n个同样的球放入m个不同的盒子中,盒子可以不分球
{
	if (n < 0)return 0;
	if (n == 0)return 1;
	return C(n - 1 + m, m - 1);
}
int solve()
{
	if (m == 1)return n;
	int least = m * (k + 1);
	if (least > n)return 0;
	int g = n - least;
	LL way = fillbox(g, m)  * (k + 1) + fillbox(g - 1, m + 1);
	return way % Z;
}
void factorial()
{
	int top = 2e6;
	fac[0] = 1; for (int i = 1; i <= top; i++)fac[i] = fac[i - 1] * i%Z;
	inv[0] = inv[1] = 1; for (int i = 2; i <= top; i++)inv[i] = inv[Z%i] * (Z - Z / i) % Z;
	for (int i = 2; i <= top; i++)inv[i] = inv[i] * inv[i - 1] % Z;
}
int main()
{
	factorial();
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d%d", &n, &m, &k);
		printf("%d\n", solve());
	}
	return 0;
}
/*
【trick&&吐槽】
敢猜想 敢打补丁

【题意】
有n(1e6)个不同的座位,围成一个圈
然后有m(m<n)个相同的人去占座
要求任意两个人之间的空位数至少为k(1000范围)
问你有多少种占座的方案

【类型】
组合数

【分析】
首先,如果我们确保座位1有人坐,那方案数为怎么算呢?
int solve()
{
	if (m == 1)return n;
	int least = m * (k + 1);
	if (least > n)return 0;
	int g = n - least; if (g == 0)return k + 1;
	LL way = C(g - 1 + m, m - 1)  * (k+1);
	way += calc();
	//way += C(g - 1 + m + 1, m) * k;
	return way % Z;
}
1,人数为1则n种坐法
2,座位数至少为m*(k+1),不够则无解
3,设剩余座位数为g,g为0则方案数为k+1
4,我们这里有了m个间隔,我们要把g个座位插入这m个间隔中

定义——
fillbox(n,m)表示n个球分配到m个盒子里的方案数,
那我们就有m-1个隔板去格挡n个球,
也就是说,n-1个球的空位中选择m-1个。
但是可以有盒子中没有球,于是我们多增加m个球使得每个盒子必定至少有一个球
所以方案数为C(n - 1 + m, m - 1)

这里是严格选1的方案数,
我们可以得到:
不选1而严格选2的方案数,
不选1、2而严格选3的方案数,
不选1、2、3而严格选4的方案数,
不选1、2、...、k而严格选k + 1的方案数,其实都为fillbox(g, m)

然而,我们不选[1~k+1],我们发现,
对于这种情况,相当于——
有一条链,长度为n-(k+1),我们从中选择m个位置坐人
然后中间有(m-1)*k的已经取走了
剩下n-(k+1)-(m-1)*k-m个数,设为w
然后我们*=fillbox(w, m + 1)就是答案

【时间复杂度&&优化】
O(log)

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值