hdu 4602 Partition 题解(组合数学,推式子)

博客介绍了如何解决hdu 4602题目,涉及将一个数n拆分为若干部分,其中k出现的次数。通过分析枚举不同情况,得出计算公式,并结合快速幂模板给出解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接:
hdu

题意简述

一个数 n n n珂以被分成若干个数的和的形式。

比如 n = 4 n=4 n=4时,分解有:
4=1+1+1+1
4=1+1+2
4=1+2+1
4=2+1+1
4=1+3
4=2+2
4=3+1
4=4

然后你要求在这些个分解中, k k k出现了多少次。

数据

输入
t//数据组数(这题多组)
n k
n k
...
n k//一次,n,k<=1e9
输出
ans
ans
...
ans//输出每个的答案,要膜1e9+7
样例

输入
2
4 2
5 5
输出
5
1

思路

我们会发现,一个数 n n n中出现 k k k,可能是一下几种情况:

  1. 在中间的情况blog1.jpg
    其中 k k k是确定的,左右两边的 a , b a,b a,b满足: a , b > 0 a,b>0 a,b>0 a + b = n − k a+b=n-k a+b=nk。然后我们发现,确定了 a a a就确定了 b b b,而且 a a a是有范围的, 0 < a < n − k 0<a<n-k 0<a<nk。所以我们考虑枚举 a a a,求出 b b b,进而求出情况数,然后求和。
    对于我们当前枚举的 a a a,情况数为:
    左边: 2 ( a − 1 ) 2^{(a-1)} 2(a1)种(把 a a a因为中间已经有 k k k了,所以 a a a想怎么拆,就怎么拆)
    中间: 1 1 1(确定了)
    右边: 2 ( b − 1 ) 2^{(b-1)} 2(b1)种(同理, b b b想怎么拆,也就怎么拆了)
    根据乘法原理,共有 2 a − 1 ∗ 2 b − 1 ∗ 1 = 2 a − 1 + b − 1 = 2 n − k − 2 2^{a-1}*2^{b-1}*1=2^{a-1+b-1}=2^{n-k-2} 2a12b11=2a1+b1=2nk2种。

我们发现这 t m tm tm的是个定值。所以我们直接乘上去就好了。 a a a ( 0 , n − k ) (0,n-k) (0,nk)之间(注意是开集),所以 a a a n − k − 2 n-k-2 nk2种取值。所以情况 1 1 1的总答案就是 2 n − k − 2 ∗ ( n − k − 1 ) 2^{n-k-2}*(n-k-1) 2nk2(nk1)

2.在两边的情况:
blog2.jpg
blog3.jpg
这两种情况其实是一样的,只是位置不一样,算出来一个,乘二就珂以了。其中一个的答案:
确定的部分( k k k): 1 1 1
不确定部分( n − k n-k nk): 2 n − k − 1 2^{n-k-1} 2nk1
然后再乘二就好了。我们会发现珂以合并同类项,变成: 2 n − k 2^{n-k} 2nk

综上,答案就是 2 n − k + 2 n − k − 2 ∗ ( n − k − 1 ) 2^{n-k}+2^{n-k-2}*(n-k-1) 2nk+2nk2(nk1)

然后就是快速幂模板了。代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define mod 1000000007ll
    #define int long long
    int qpow(int a,int b,int m=mod)
    {
        if (b<0) return 0;
        int r=1;
        while(b)
        {
            if (b&1)
            {
                r=r*a%m;
            }
            a=a*a%m;
            b>>=1;
        }
        return r;
    }

    int n,k;
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void P1(int x)
    {
        if (x>=0 and x<=9)
        {
            putchar(x+'0');
            return;
        }
        P1(x/10);
        putchar(x%10+'0');
    }
    void Input()
    {
        R1(n),R1(k);
    }
    void Soviet()
    {
        if (n==k)
        {
            puts("1");
            return;
        }
        int ans=qpow(2,n-k);
        ans+=qpow(2,n-k-2)*(n-k-1);
        ans%=mod;
        P1(ans);putchar('\n');
    }
    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        int t;R1(t);
        while(t--)
        {
            Input();
            Soviet();
        }
    }
    #undef int //long long
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值