「KDOI-04」Pont des souvenirs(组合数学+思维)

97 篇文章 0 订阅

「KDOI-04」Pont des souvenirs

题目背景

虽然这是一个 C,但是

题目描述

给定正整数 n , k n,k n,k,求有多少个长度为 n n n 的正整数序列 a a a 满足:

  • 0 < a 1 ≤ a 2 ≤ a 3 ≤ ⋯ ≤ a n ≤ k 0<a_1\le a_2\le a_3\le\cdots\le a_n\le k 0<a1a2a3ank
  • ∀   i ≠ j \forall\ i\not=j  i=j a i + a j ≤ k + 1 a_i+a_j\le k+1 ai+ajk+1

答案对 1 0 9 + 7 10^9+7 109+7 取模。

输入格式

本题包含多组测试数据。

输入的第一行包含一个正整数 T T T,表示测试数据组数。

对于每组测试数据,输入包含一行两个正整数 n , k n,k n,k

输出格式

对于每组测试数据,输出一行一个整数表示答案。

样例 #1

样例输入 #1

5
2 2
1 3
4 5
4030 218
1145 1419

样例输出 #1

2
3
20
571656908
172735629

提示

【样例解释】

对于第 1 1 1 组测试数据,所有满足要求的序列为 ( 1 , 1 ) (1,1) (1,1) ( 1 , 2 ) (1,2) (1,2)

对于第 2 2 2 组测试数据,所有满足要求的序列为 ( 1 ) (1) (1) ( 2 ) (2) (2) ( 3 ) (3) (3)

【数据范围】

对于 100 % 100\% 100% 的数据,保证 1 ≤ T ≤ 2 × 1 0 5 1\le T\le2\times10^5 1T2×105 1 ≤ n , k ≤ 1 0 7 1\le n,k\le10^7 1n,k107

本题开启捆绑测试。

子任务编号分值 T ≤ T\le T n ≤ n\le n k k k
1 1 1 8 8 8 5 5 5 5 5 5 ≤ 5 \le5 5
2 2 2 3 3 3 1 0 5 10^5 105 1 0 7 10^7 107 = 1 =1 =1
3 3 3 3 3 3 1 0 5 10^5 105 1 0 7 10^7 107 = 2 =2 =2
4 4 4 8 8 8 1 0 5 10^5 105 1 0 7 10^7 107 = 3 =3 =3
5 5 5 16 16 16 10 10 10 200 200 200 ≤ 200 \le200 200
6 6 6 16 16 16 10 10 10 3000 3000 3000 ≤ 3000 \le3000 3000
7 7 7 8 8 8 1 0 4 10^4 104 1 0 7 10^7 107 ≤ 5 \le5 5
8 8 8 8 8 8 100 100 100 1 0 7 10^7 107 ≤ 1 0 5 \le10^5 105
9 9 9 30 30 30 2 × 1 0 5 2\times10^5 2×105 1 0 7 10^7 107 ≤ 1 0 7 \le10^7 107

思路

  • 首先,我们为了满足条件: 0 < a 1 ≤ a 2 ≤ a 3 ≤ ⋯ ≤ a n ≤ k 0<a_1\le a_2\le a_3\le\cdots\le a_n\le k 0<a1a2a3ank,对于这种单调的,我们可以采用隔板法来做,因此在没有条件 2 2 2 的约束下,这样的方案数为: C n + k − 1 k − 1 C_{n+k-1}^{k-1} Cn+k1k1

  • 为什么是这样呢?因为我们发现:

    x i x_i xi 表示 i i i 在序列种出现的个数。

    那么 ∑ i = 1 n x i = n ( x i ≥ 0 ) \sum_{i=1}^{n} x_i=n(x_i \ge 0) i=1nxi=n(xi0)

    接着,我们再令 y i y_i yi x i + 1 x_i+1 xi+1

    此时: ∑ i = 1 k y i = n + k ( y i ≥ 1 ) \displaystyle\sum_{i=1}^{k}y_i=n+k(y_i\ge 1) i=1kyi=n+k(yi1)

因此这就是隔板法的推导。

  • 那么此时我们加上条件 2 2 2,那么我们就得优先分析 a n a_n an 了,我们就暴力枚举它,我们就可以得到: 0 ≤ a 1 ≤ a 2 ≤ ⋯ ≤ a n − 1 ≤ min ⁡ { a n , k + 1 − a n } 0\le a_1\le a_2\le\cdots\le a_{n-1}\le\min\{a_n, k+1-a_n\} 0a1a2an1min{an,k+1an}。那么此时的方案数为: ∑ i = 1 k C n − 1 + min ⁡ { i , k + 1 − i } − 1 min ⁡ { i , k + 1 − i } − 1 \sum_{i=1}^{k}C_{n-1+\min\{i, k+1-i\}-1}^{\min\{i,k+1-i\}-1} i=1kCn1+min{i,k+1i}1min{i,k+1i}1

  • 但我们发现,直接枚举的话,时间复杂度很高,因此我们尝试化简上述式子:

  • 拓展:(很重要)
    - C n m = C n n − m C_{n}^{m}=C_{n}^{n-m} Cnm=Cnnm
    - ∑ i = 0 m C i n = C m + 1 n + 1 \displaystyle\sum_{i=0}^{m}C_{i}^{n}=C_{m+1}^{n+1} i=0mCin=Cm+1n+1

    • 我们考虑 min ⁡ { i , k + 1 − i } \min\{i, k+1-i\} min{i,k+1i} 的取值范围。
      • 我们令 m = k + 1 m=k+1 m=k+1
      • 则变成了 min ⁡ { i , m − i } \min\{i, m-i\} min{i,mi} 的取值范围。
      • 1 ∼ ⌈ m 2 ⌉ 1\sim \left\lceil\frac{m}{2}\right\rceil 12m 1 ∼ ⌊ m 2 ⌋ 1\sim \left\lfloor\frac{m}{2}\right\rfloor 12m
        <=> a n s = ∑ i = 1 ⌊ k + 1 2 ⌋ C n + i − 2 i − 1 + ∑ i = 1 ⌈ k + 1 2 ⌉ C n + i − 2 i − 1 ans=\displaystyle\sum_{i=1}^{\left\lfloor\frac{k+1}{2}\right\rfloor}C_{n+i-2}^{i-1}+\displaystyle\sum_{i=1}^{\left\lceil\frac{k+1}{2}\right\rceil}C_{n+i-2}^{i-1} ans=i=12k+1Cn+i2i1+i=12k+1Cn+i2i1
        <=>我们枚举 i − 1 i-1 i1 ∑ i = 0 ⌊ k + 1 2 ⌋ − 1 C n + i − 1 i + ∑ i = 0 ⌈ k + 1 2 ⌉ − 1 C n + i − 1 i \displaystyle\sum_{i=0}^{\left\lfloor\frac{k+1}{2}\right\rfloor-1}C_{n+i-1}^{i}+\displaystyle\sum_{i=0}^{\left\lceil\frac{k+1}{2}\right\rceil-1}C_{n+i-1}^{i} i=02k+11Cn+i1i+i=02k+11Cn+i1i
        <=> 根据 C n m = C n n − m C_{n}^{m}=C_{n}^{n-m} Cnm=Cnnm,此时: ∑ i = 0 ⌊ k + 1 2 ⌋ − 1 C n + i − 1 n + ∑ i = 0 ⌈ k + 1 2 ⌉ − 1 C n + i − 1 n \displaystyle\sum_{i=0}^{\left\lfloor\frac{k+1}{2}\right\rfloor-1}C_{n+i-1}^{n}+\displaystyle\sum_{i=0}^{\left\lceil\frac{k+1}{2}\right\rceil-1}C_{n+i-1}^{n} i=02k+11Cn+i1n+i=02k+11Cn+i1n
        <=> 根据 ∑ i = 0 m C i n = C m + 1 n + 1 \displaystyle\sum_{i=0}^{m}C_{i}^{n}=C_{m+1}^{n+1} i=0mCin=Cm+1n+1,柿子就变成了 C n + ⌊ k + 1 2 ⌋ n + C n + ⌈ k + 1 2 ⌉ n C_{n+\left\lfloor\frac{k+1}{2}\right\rfloor}^{n}+C_{n+\left\lceil\frac{k+1}{2}\right\rceil}^{n} Cn+2k+1n+Cn+2k+1n
        以上思路参考

细节

  • 细节1:开 long long。
  • 细节2:因为这个数据范围在 1 0 6 10^6 106 1 0 7 10^7 107 之间,而且还有取模,我们就可以用逆元法求组合数的方法来做。
  • 细节3:那个 N N N 最好开: 15000000 15000000 15000000经验,这个调了很久

AC 代码

#include<iostream>
#include<algorithm>
#include<cstring>

typedef long long ll;

using namespace std;

const int N = 15100000 , mod = 1e9+7;

ll fac[N],infac[N];
int T,n,k;

ll qmi(ll a,ll b){
    ll res=1;
    
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

ll C(ll a,ll b){
    a=a+b-1,b=b-1;
    if(b>a)return 0;
    return fac[a]*infac[b]%mod*infac[a-b]%mod;
}

void init(){
    infac[0]=fac[0]=1;
    
    for(register int i=1;i<=N-10;i++){
        fac[i]=fac[i-1]*i%mod;
        infac[i]=infac[i-1]*qmi(i,mod-2)%mod;
    }
}

int main(){
    scanf("%d",&T);
    
    init();
    
    while(T--){
        scanf("%d%d",&n,&k);
        
        //隔板法
        
        if(k&1){
            int p=(k+1)>>1;
            printf("%d\n",(C(n,p)+C(n,p-1))%mod);
        }else{
            int p=(k+1)>>1;
            printf("%d\n",(C(n,p)+C(n,p))%mod);
        }
        
    }
    
    return 0;
}
  • 22
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值