NOIP2016提高组 Day2 T1 组合数问题

Posted $mathit{AlphaINF}$

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2016提高组 Day2 T1 组合数问题相关的知识,希望对你有一定的参考价值。

题目传送门:https://www.luogu.org/problemnew/show/P2822                 ↓题目大意↓

数据的极限范围:n,m≤2000,k≤21,数据组数≤10000。

由于此题k不大于21,故在计算组合数Cij时,并不需要存储它的真实数值,只需要存储其≤19的所有素因子的个数,判断Cij是否为k的倍数,仅需要判断Cij中各素因子的个数是否大于等于k中的个数即可。基于组合数的性质,我们如果要求出Cij,我们可以通过Ci(j-1)乘上i-j+1然后再除以j即可得到。

下面来考虑如何乘以或除以一个数x。若需要在Ci(j-1)的基础上乘以x,可以考虑将x分解质因数,仅将其≤19的全部素因子与Ci(j-1)的素因子个数进行累加。除法同理,加法改成减法即可。

最后维护一个二维数组b。若b[i][j]=1,则表示Cij是k的倍数。输入n,m时,将b[1..n][1..m]进行累加即可得出答案。很明显这么操作依然会TLE,使用另一数组维护b[i][j]的前缀和即可。

时间复杂度为O(n*m+T)。 但常数很大(本地均为0.3s左右)。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define M 2000
 5 using namespace std;
 6 int p[]={2,3,5,7,11,13,17,19};
 7 struct cg{
 8     int a[8];
 9     cg(){memset(a,0,sizeof(a));}
10     cg(int x){
11         for(int i=0;i<8;i++) 
12         while(x%p[i]==0) a[i]++,x/=p[i];
13     }
14     friend cg operator *(cg a,int x){
15         for(int i=0;i<8;i++) 
16         while(x%p[i]==0) a.a[i]++,x/=p[i];
17         return a;
18     }
19     friend cg operator /(cg a,int x){
20         for(int i=0;i<8;i++) 
21         while(x%p[i]==0) a.a[i]--,x/=p[i];
22         return a;
23     }
24     friend bool operator +(cg a,int x){
25         cg c=a;
26         for(int i=0;i<8;i++) 
27         while(x%p[i]==0){
28             c.a[i]--;x/=p[i];
29             if(c.a[i]<0) return 0;
30         } 
31         return 1;
32     }
33 }a[M+10][M+10];
34 int b[M+10][M+10]={0},k;
35 
36 void init(){
37     for(int i=1;i<=M;i++){
38         int zhi=i>>1;
39         for(int j=1;j<=zhi;j++)
40         a[i][j]=a[i][i-j]=a[i][j-1]*(i-j+1)/j;
41     }
42     for(int i=1;i<=M;i++){
43         int zhi=i>>1;
44         for(int j=1;j<=zhi;j++){
45             b[i][j]=b[i][i-j]=a[i][j]+k;
46         }
47     }
48     for(int i=1;i<=M;i++)
49     for(int j=1;j<=M;j++){
50         b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+b[i][j];
51     }
52 }
53 
54 int main(){
55     freopen("problem.in","r",stdin);
56     freopen("problem.out","w",stdout);
57     int cas; cin>>cas>>k;
58     init();
59     while(cas--){
60         int x,y; scanf("%d%d",&x,&y);
61         printf("%d\\n",b[x][y]);
62     }
63 }

 

以上是关于NOIP2016提高组 Day2 T1 组合数问题的主要内容,如果未能解决你的问题,请参考以下文章

AC日记——组合数问题 落谷 P2822 noip2016day2T1

noip 2016 提高组试题看法

Noip2016day2解题报告

NOIP2015提高组Day2 T1 跳石头

noip 2016 提高组总结(不是题解)

[NOIP2016提高组]组合数问题