【NOIP模拟考一】组合数学 day1 third 子集

【问题描述】

azui大爷在quack大爷的带领下开始玩集合了,可是他太懒了,不想做quack大爷布置的作业题,便拿来给你做了:

S 集合中有n个不同的元素,我们从1-n标号。考虑S 的子集Si,j,将这些子集排成一个r行c列矩阵的样子。

其中第一行为S1,1,S1,2,…,S1,c,第二行为S2,1,S2,2,..,S2,c一直到第r行为Sr,1, Sr,2,…, Sr,c。

这些集合还满足对于在一行中左右相邻的两个集右,左侧是右侧

的子集,即Si,j∈Si,j+1。

这些集合还满足对于在一列中上下相邻的两个集合,上方是下方

的子集,即Si,j∈Si+1,j。

问对于S 的全部子集,有多少可能的情况排成上述的矩阵(每个子集可以重复使用),结果模109 +7 输出。

你一定知道这么简单的题目怎么做,快帮帮azui大爷吧。

【输入格式】

一行三个数n,r,c.

【输出格式】

      一个数表示答案。

【样例输入】

1 2 2

【样例输出】

6

【样例解释】

1

2

3

4

如上图标号后,有:1是2和3的子集,1,2,3都是4的子集。

用0表示空集,1表示元素1,从左到右是1、2、3、4的话,有以下6种情况

0000 0001 0011 0101 0111 1111

 

【数据范围】

对于10%的数据,r*c*n <= 16

对于20%的数据,r*c <= 16, n <= 100

对于另20%的数据,r=1, c<=1000000

对于另10%的数据,n=1

对于100%的数据,r, c <= 1000000, n <= 10^9

    对这道题我是萌比的,于是考试的时候只能悄悄地去骗一骗子任务的分QWQ

    一号子任务显然是要让我们dfs不说了

    二号子任务其实是让我们先将n=1去dfs再n次方也不说了(这里的n次方思路是正解的一部分哦)

    三号子任务退化成一维后也不见得很简单,我们先尝试看看。由于不同元素之间并无交集,所以可以先将n等于1,再n次方也没有关系。那么我们观察,由于n=1了,所以要么是0,要么是1并且一定是一段0之后全是1,其实就有c+1种情况(0个1,1个,2个1...c个1),于是便是(c+1)^n并不困难。(这里是正解的一维版本哦)

    四号子任务可以DP。。。(当然好像还是是超时的)

    好了,研究了子任务后,我们来看看这与正解有何关联。首先必须将三号子任务扩展成二维,那么可以发现:每一种合法解都是呈现每一行的1个数逐渐递增,就像下面的图这样:可以发现,这个排列成了一条折线,于是便有了折线的生成:C(r+c,r)(由于两端开始点都不确定,所以不是C(r+c-2,r-1))


    那么只需要用我们的组合数阶乘法就好了~代码如下:

//%>_<%
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
#define mod 1000000007ll
ll r,c,n,sum=1;
ll ksm(ll a,ll k)
{
	if(!k)
		return 1;
	ll p=ksm(a,k/2);
	if(k&1)return p*p%mod*a%mod;
	return p*p%mod;
}
ll jc(ll a)
{
	ll ans=1;
	for(ll i=1;i<=a;i++)
		ans=ans*i%mod;
	return ans;
}
ll C(ll a,ll b)
{
	return jc(a)*ksm(jc(b),mod-2)%mod*ksm(jc(a-b),mod-2)%mod;
}
int main()
{
	scanf("%lld%lld%lld",&n,&r,&c);
	if(c<r)
		swap(c,r);
	printf("%lld",ksm(C(r+c,r),n));
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值