[BZOJ3377]geng4512膜你题1:子集

xiaoxiao2021-02-28  80

【问题描述】

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

题解:线性求逆元:

i*A[i] ≡ 1 (mod p), 求i的逆元A[i]

A[i] ≡ i^(-1) (mod p) 令p=k*i+r ( r<i,1<i<p ) k*i+r≡0 (mod p) (k*i+r) * i^(−1)*r^(−1) ≡ 0 * i^(−1)*r^(−1) (mod p) k*r^(−1)+i^(−1) ≡ 0 (mod p) i^(−1) ≡ -k*r^(−1) (mod p)

i^(−1) ≡ -(p/i)*(p%i)^(−1) (mod p)

即:A[i]=-(p/i)*A[p%i];

#include<cstdio> typedef long long LL; const int Mod=1e9+7; LL n, r, c; LL Mul( LL a, LL b ) { LL res=0; for( int i=1; i<=b; i<<=1, a=(a<<1)%Mod ) if( b&i ) res=(res+a)%Mod; return res; } LL Ksm( LL b, LL p ) { LL res=1; while(p) { if( p&1 ) res=Mul( res, b ); b=Mul( b, b ); p>>=1; } return res; } LL Fac( LL w ) {//阶乘factorial LL res=1; for( LL i=2; i<=w; i++ ) res=res*i%Mod; return res; } LL Inv( LL w ) {//求逆元 if( w==1 ) return 1; return ( Mod-Mod/w*Inv( Mod%w )%Mod )%Mod;//(num+Mod)%Mod避免负数 } LL C( LL n, LL m ) {//C( c+r, r )%Mod return Fac(n) * Inv( Fac(m) )%Mod * Inv( Fac(n-m) )%Mod; /*C(n,m)%Mod == n! / (m!*(n-m)!) % Mod == n! * (m!)^(-1) * ((n-m)!)^(-1) % Mod == n! * Inv(m!) * Inv((n-m)!) % Mod*/ } int main() { scanf( "%I64d%I64d%I64d", &n, &r, &c ); printf( "%I64d\n", Ksm( C(r+c,r), n ) ); return 0; }

转载请注明原文地址: https://www.6miu.com/read-75000.html

最新回复(0)