1. 程式人生 > >【2018/10/16測試T1】膜法

【2018/10/16測試T1】膜法

【題目】

內網傳送門
外網傳送門


【分析】

做這道題之前,先儲備一些關於組合數的知識吧
C n m = C n n

m C_n^m=C_n^{n-m}
C n m =
C n 1 m + C n
1
m 1
C_n^m=C_{n-1}^m+C_{n-1}^{m-1}

C n 0 + C n 1 + C n 2 + . . . + C n n = 2 n C_n^0+C_n^1+C_n^2+...+C_n^n=2^n
注:這些公式可能對下面的題解無關,但對自己推公式的話應該是有一定幫助的

30 pts:

根據乘法原理,最後的答案為每個環節的方案數乘起來。

根據加法原理,每個環節的方案書為 j = l      r    C            j k i + j l \sum_{j=l}^{\;\;r} \;C_{\;\;\;\;\;j}^{k_i+j-l}

可以直接預處理出組合數,然後 O( n m nm )暴力列舉求解即可。

60 pts:

容易發現我們加的是組合數表中一個斜線上的所有組合數。

O ( n 2 ) (n^2) 預處理組合數及其斜線上的字首和,然後 O ( m ) (m) 統計。

100 pts:

注意到以下公式的轉換:

j = l r C            j k i + j l = j = l r C      j l k i = C        r + 1 l k i + 1 C            l l k i + 1 \sum_{j=l}^{r}C_{\;\;\;\;\;j}^{k_i+j-l}=\sum_{j=l}^{r}C_{\;\;j}^{l-k_i}=C_{\;\;\;r+1}^{l-k_i+1}-C_{\;\;\;\;\;l}^{l-k_i+1}

因此我們只需求兩個組合數即可,預處理出階乘和階乘的逆元即可


【程式碼】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define Mod 1000000007
using namespace std;
int fac[N],inv[N];
int dec(int x,int y)  {return (x-y+Mod)%Mod;}
int mul(int x,int y)  {return 1ll*x*y%Mod;}
int C(int n,int m)  {return mul(fac[n],mul(inv[m],inv[n-m]));}
int Power(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1)
		  ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}
int main()
{
//	freopen("m.in","r",stdin);
//	freopen("m.out","w",stdout);
	int n,m,i,j,l,r,k,ans=1;
	scanf("%d%d",&n,&m);
	fac[0]=fac[1]=1;
	for(i=2;i<=n+1;++i)  fac[i]=mul(fac[i-1],i);
	inv[n+1]=Power(fac[n+1],Mod-2);
	for(i=n;i>=0;--i)  inv[i]=mul(inv[i+1],i+1);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d",&l,&r,&k);
		ans=mul(ans,dec(C(r+1,l-k+1),C(l,l-k+1)));
	}
	printf("%d",ans);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}