[Luogu P4104] [BZOJ 3612] [HEOI2014]平衡

洛谷传送门
BZOJ传送门

题目描述

下课了,露露、花花和萱萱在课桌上用正三棱柱教具和尺子摆起了一个“跷跷板”。

这个“跷跷板”的结构是这样的:底部是一个侧面平行于地平面的正三棱柱教具,上面 摆着一个尺子,尺子上摆着若干个相同的橡皮。尺子有 2 n + 1 2n + 1 2n+1 条等距的刻度线,第 n + 1 n + 1 n+1 条 刻度线恰好在尺子的中心,且与正三棱柱的不在课桌上的棱完全重合。

露露发现这个“跷跷板”是不平衡的(尺子不平行于地平面)。于是,她又在尺子上放 了几个橡皮,并移动了一些橡皮的位置,使得尺子的 2 n + 1 2n + 1 2n+1 条刻度线上都恰有一块相同质 量的橡皮。“跷跷板”平衡了,露露感到很高兴。

花花觉得这样太没有意思,于是从尺子上随意拿走了 k k k 个橡皮。令她惊讶的事情发生了: 尺子依然保持着平衡! 萱萱是一个善于思考的孩子,她当然不对尺子依然保持平衡感到吃惊,因为这只是一个 偶然的事件罢了。令她感兴趣的是,花花有多少种拿走 k k k 个橡皮的方法,使得尺子依然保 持平衡?当然,为了简化问题,她不得不做一些牺牲——假设所有橡皮都是拥有相同质量的 质点。但即使是这样,她也没能计算出这个数目。放学后,她把这个问题交给了她的哥哥/ 姐姐——Hibarigasaki 学园学生会会长,也就是你。当然,由于这个问题的答案也许会过于 庞大,你只需要告诉她答案 m o d      p \mod\ p mod p 的值。

输入输出格式

输入格式:

第一行,一个正整数,表示数据组数 T T T(萱萱向你询问的次数)。

接下来 T T T 行,每行 3 3 3 个正整数 n , k , p n, k, p n,k,p

输出格式:

T T T 行,每行一个正整数,代表你得出的对应问题的答案。

输入输出样例

输入样例#1:
10 6 5 10000  
4 1 10000 
9 6 10000 
4 6 10000 
5 1 10000 
8318 10 9973 
9862 9 9973 
8234 9 9973 
9424 9 9973 
9324 9 9973
输出样例#1:
73
1
920
8
1
4421
2565
0
446
2549

说明

10%的数据满足: n ≤ 10 n \le 10 n10

30%的数据满足: n ≤ 50 n \le 50 n50

50%的数据满足: n ≤ 1000 n \le 1000 n1000

另有 10%的数据满足: k = 3 k = 3 k=3

在此基础上,另有 10%的数据满足: p = 2 p = 2 p=2

100%的数据满足: T ≤ 20 T \le 20 T20 1 ≤ n ≤ 10000 1 \le n \le 10000 1n10000 1 ≤ k ≤ 10 1 \le k \le 10 1k10 2 ≤ p ≤ 10000 2 \le p \le 10000 2p10000,且 k ≤ 2 n + 1 k \le 2n+1 k2n+1

解题分析

显然题目等同于从 1 ∼ 2 n + 1 1\sim 2n+1 12n+1中选出 k k k个不同的数, 满足其和为 k ( n + 1 ) k(n+1) k(n+1)的方案数。

转化一下可知, 我们只需要知道从 1 ∼ n 1\sim n 1n中选出若干数, 其和等于 K K K的方案数即可, 其他部分等于只取 − n ∼ − 1 -n\sim -1 n1的负数抵消即可。

那么设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示选了 i i i个互不相同的数, 其和为 j j j的方案数。 考虑如何满足互不相同这个条件。 我们通过序列全部加 1 1 1和向序列头部插入一个 1 1 1即可满足。

那么就有:
d p [ i ] [ j − i ] → d p [ i ] [ j ] d p [ i − 1 ] [ j − i ] → d p [ i ] [ j ] dp[i][j-i]\to dp[i][j] \\ dp[i-1][j-i]\to dp[i][j] dp[i][ji]dp[i][j]dp[i1][ji]dp[i][j]
但是这样还是有点小问题: 实际上这样在 i i i相等的时候转移会导致最大的数超过 n n n, 那么我们再减去 d p [ i − 1 ] [ j − n − 1 ] dp[i-1][j-n-1] dp[i1][jn1]就好了。

注意 0 0 0也是可以取的, 特判。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int dp[11][MX];
int T, n, k, MOD;
int main(void)
{
	in(T); int ans, mx;
	W (T--)
	{
		in(n), in(k), in(MOD);
		ans = 0, mx = n * k;
		dp[0][0] = 1;
		for (R int i = 1; i <= k; ++i)
		for (R int j = i; j <= mx; ++j)
		{
			dp[i][j] = dp[i][j - i] + dp[i - 1][j - i];
			if (j > n) dp[i][j] -= dp[i - 1][j - n - 1];
			dp[i][j] %= MOD;
		}
		for (R int i = 0; i <= k; ++i)
		for (R int j = i; j <= mx; ++j)
		{
			(ans += dp[i][j] * dp[k - i][j] % MOD) %= MOD;
			if (i < k) (ans += dp[i][j] * dp[k - i - 1][j] % MOD) %= MOD;
		}
		printf("%d\n", ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值