版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/85015960
题目描述:
4=1+1+1+1 =1+1+2 =1+2+1 =2+1+1 =1+3 =3+1 =4
上述对正整数4的拆分中1出现了12次,现给出
n,k≤109,求对n的拆分中数字k出现的次数
多组数据,T<=10000
题目分析:
首先,对一个数n的所有拆分方案总共有
2n−1种,相当于在n相同的小球中插入p块隔板(p=0,1,2…n-1),那么总方案数为
Cn−10+Cn−11+⋯+Cn−1n−1=(1+1)n−1=2n−1
由于k在同一个拆分中可能出现多次,不好处理,我们先考虑只计算一次的情况
-
n<k 输出0
-
n=k 输出1
-
n=k+1 输出2
-
n>k+1
这时k倘若在靠左边的位置,则右边的数随意拆分,数量为
2n−k−1,k靠右同理
倘若k在中间,则有n-k-1个位置可选,设两边的数为t1,t2,则拆分方案数为
2t1−1∗2t2−1=2n−k−2
总的加起来就是
2n−k+(n−k−1)∗2n−k−2=(n−k+3)∗2n−k−2种
但是,此时我们只求出了一个k的情况,同一种拆分中k出现多次怎么办?
其实思考一下,问题已经无意中解决了,若同一种拆分中有p个k,那么这种拆分就会被统计到p次,因为我们并没有管除了当前的k之外其他地方还会不会出现k,举个例子:
8=1+
2
+
2
+
2
+1 这种拆分中,在
2,
2,
2时各统计了一次,系数问题已经解决了
总结一下,有些时候统计问题的去重操作其实很简单,对称的感觉很重要
比如这道题,看似会算重的统计方式实际上把次数也求出来了。
PS:要是在考场上,直接列表格:
n,k1234511251228212512312541251
很容易发现只跟n-k的大小有关
#include<cstdio>
const int mod = 1e9+7;
int ksm(int a,int b)
{
int s=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
return s;
}
int main()
{
int T,n,k;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
if(n<k) puts("0");
else if(n<=k+1) printf("%d\n",n-k+1);
else printf("%d\n",1ll*(n-k+3)*ksm(2,n-k-2)%mod);
}
}