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

来源:互联网 发布:有道云协作 mac 编辑:程序博客网 时间:2024/05/16 11:44

【问题描述】

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 1000000007llll 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));}


原创粉丝点击