BZOJ 3612: [Heoi2014]平衡

Description

下课了,露露、花花和萱萱在课桌上用正三棱柱教具和尺子摆起了一个“跷跷板”。
这个“跷跷板”的结构是这样的:底部是一个侧面平行于地平面的正三棱柱教具,
上面 摆着一个尺子,尺子上摆着若干个相同的橡皮。尺子有 2n + 1 条等距的刻度线,
第 n + 1 条 刻度线恰好在尺子的中心,且与正三棱柱的不在课桌上的棱完全重合。
露露发现这个“跷跷板”是不平衡的(尺子不平行于地平面)。于是,她又在尺
子上放 了几个橡皮,并移动了一些橡皮的位置,使得尺子的 2n + 1 条刻度线上都恰
有一块相同质 量的橡皮。“跷跷板”平衡了,露露感到很高兴。
花花觉得这样太没有意思,于是从尺子上随意拿走了 k 个橡皮。令她惊讶的事
情发生了: 尺子依然保持着平衡!
萱萱是一个善于思考的孩子,她当然不对尺子依然保持平衡感到吃惊,因为这
只是一个 偶然的事件罢了。令她感兴趣的是,花花有多少种拿走 k 个橡皮的方法
,使得尺子依然保 持平衡?
当然,为了简化问题,她不得不做一些牺牲——假设所有橡皮都是拥有相同质量的
质点。但即使是这样,她也没能计算出这个数目。放学后,她把这个问题交给了她
的哥哥/ 姐姐——Hibarigasaki 学园学生会会长,也就是你。当然,由于这个问题
的答案也许会过于 庞大,你只需要告诉她答案 mod p 的值。

Input

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

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

Output

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

Sample Input

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

Sample Output

73

1

920

8

1

4421

2565

0

446

2549

HINT

T <= 20,1 <= n <= 10000,1 <= k <= 10,2 <= p <= 10000,且 k <= 2n+1。
#分析
我们设f[i][j]表示取了i个不同的数和为j的方案,我们有一个显然的想法就是集合合并,但是似乎很不优秀,所以我们考虑这样子想,假如对于一个可行的集合来说,我把所有的数都增加1的话显然这些数还是不同的,我们也可以把所有的数都增加1然后再加入1这个数字,也是合法的,但是这样子的话我们很有可能加到超过n,所以我们直接减去就好
#代码

#include <bits/stdc++.h>

#define N 100005
#define M 15

int max(int x,int y)
{
    return x > y ? x : y;
}

int min(int x,int y)
{
    return x < y ? x : y;
}

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int f[M][N];

int main()
{
    int T = read();
    while (T--)
    {
        int n = read(), m = read(), mod = read();
        f[0][0] = 1;
        for (int i = 1; i <= m; i++)
            for (int j = i; j <= n * m; j++)
            {
                f[i][j] = f[i - 1][j - i] + f[i][j - i];
                if (j > n)
                    f[i][j] -= f[i - 1][j - n - 1] - mod;
                while (f[i][j] >= mod)
                    f[i][j] -= mod;
            }
        int ans = 0;
        for (int i = 0; i <= m; i++)
            for (int j = 0; j <= n * m; j++)
            {
                (ans += f[i][j] * f[m - i][j]) %= mod;
                if (i < m)
                    (ans += f[i][j] * f[m - 1 - i][j]) %= mod;
            }
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值