HDU4602 Partition (神奇的计算方式)

题目描述:

4 = 1 + 1 + 1 + 1     = 1 + 1 + 2     = 1 + 2 + 1     = 2 + 1 + 1     = 1 + 3     = 3 + 1     = 4 4=1+1+1+1\\~~~=1+1+2\\~~~=1+2+1\\~~~=2+1+1\\~~~=1+3\\~~~=3+1\\~~~=4 4=1+1+1+1   =1+1+2   =1+2+1   =2+1+1   =1+3   =3+1   =4
上述对正整数4的拆分中1出现了12次,现给出 n , k ≤ 1 0 9 n,k\le10^9 n,k109,求对n的拆分中数字k出现的次数
多组数据,T<=10000

题目分析:

首先,对一个数n的所有拆分方案总共有 2 n − 1 2^{n-1} 2n1种,相当于在n相同的小球中插入p块隔板(p=0,1,2…n-1),那么总方案数为 C n − 1 0 + C n − 1 1 + ⋯ + C n − 1 n − 1 = ( 1 + 1 ) n − 1 = 2 n − 1 C_{n-1}^0+C_{n-1}^1+\cdots +C_{n-1}^{n-1}=(1+1)^{n-1}=2^{n-1} Cn10+Cn11++Cn1n1=(1+1)n1=2n1
由于k在同一个拆分中可能出现多次,不好处理,我们先考虑只计算一次的情况

  • n<k 输出0

  • n=k 输出1

  • n=k+1 输出2

  • n>k+1
    这时k倘若在靠左边的位置,则右边的数随意拆分,数量为 2 n − k − 1 2^{n-k-1} 2nk1,k靠右同理
    倘若k在中间,则有n-k-1个位置可选,设两边的数为t1,t2,则拆分方案数为 2 t 1 − 1 ∗ 2 t 2 − 1 = 2 n − k − 2 2^{t1-1}*2^{t2-1}=2^{n-k-2} 2t112t21=2nk2
    总的加起来就是 2 n − k + ( n − k − 1 ) ∗ 2 n − k − 2 = ( n − k + 3 ) ∗ 2 n − k − 2 2^{n-k}+(n-k-1)*2^{n-k-2}=(n-k+3)*2^{n-k-2} 2nk+(nk1)2nk2=(nk+3)2nk2

    但是,此时我们只求出了一个k的情况,同一种拆分中k出现多次怎么办?
    其实思考一下,问题已经无意中解决了,若同一种拆分中有p个k,那么这种拆分就会被统计到p次,因为我们并没有管除了当前的k之外其他地方还会不会出现k,举个例子:
    8 = 1 + 8=1+ 8=1+ 2 \color{red}2 2 + + + 2 \color{green}2 2 + + + 2 \color{blue}2 2 + 1 +1 +1 这种拆分中,在 2 \color{red}2 2 2 \color{green}2 2 2 \color{blue}2 2时各统计了一次,系数问题已经解决了

总结一下,有些时候统计问题的去重操作其实很简单,对称的感觉很重要
比如这道题,看似会算重的统计方式实际上把次数也求出来了。

PS:要是在考场上,直接列表格:
n , k 1 2 3 4 5 1 1 2 2 1 3 5 2 1 4 12 5 2 1 5 28 12 5 2 1 \begin{array}{c|ccccc} n,k&amp;1&amp;2&amp;3&amp;4&amp;5\\\hline 1&amp;1\\ 2&amp;2&amp;1\\ 3&amp;5&amp;2&amp;1\\ 4&amp;12&amp;5&amp;2&amp;1\\ 5&amp;28&amp;12&amp;5&amp;2&amp;1 \end{array} 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);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值