六省联考:组合数问题
Posted ck666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了六省联考:组合数问题相关的知识,希望对你有一定的参考价值。
4870: [Shoi2017]组合数问题
2017-09-03
Description
Input
第一行有四个整数 n, p, k, r,所有整数含义见问题描述。
1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 ? 1
Output
一行一个整数代表答案。
INPUT_1
2 10007 2 0
INPUT_2
20 10007 20 0
OUT_1
8
OUT_2
176
并不知道这个是什么玄学(组合数),但是这个题并不是裸组合数,因为联考时用组合数递推只得了30poi....
然后因为k比较小,n比较大,Lucas定理就不能用了,乘法逆元只能解决n<=10^5的T;显然这个题不能用
只能回归组合数定义f[i][j]=f[i-1][j]+f[i-1][(j-1+k)%k]
用矩阵加速这个过程。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> #define ll long long using namespace std; ll read(){ ll an=0,f=1;char ch=getchar(); while(!(‘0‘<=ch&&ch<=‘9‘)){if(ch==‘-‘)f=-f;ch=getchar();} while(‘0‘<=ch&&ch<=‘9‘){an=an*10+(ch-‘0‘);ch=getchar();} return an*f; } struct saber{ ll a[60][60]; }ans,d; ll n,p; int k,r; saber operator *(saber A,saber B){ saber c; for(int i=0;i<=50;i++) for(int j=0;j<=50;j++)c.a[i][j]=0; for(int i=0;i<k;i++) for(int j=0;j<k;j++) for(int l=0;l<k;l++) c.a[i][j]=(c.a[i][j]+A.a[i][l]*B.a[l][j])%p; if(k==1)c.a[0][0]=((ll)A.a[0][0]*B.a[0][0]+c.a[0][0])%p; return c; } saber kp(ll kk){ saber f; for(int i=0;i<=50;i++) for(int j=0;j<=50;j++)f.a[i][j]=0; for(ll i=0;i<k;i++)f.a[i][i]=1; while(kk){ if(kk&1)f=f*d; d=d*d; kk>>=1; } return f; } int main(){ n=read();p=read();k=read();r=read(); for(int i=0;i<k;i++){ d.a[i][i]=1; d.a[i][(i-1+k)%k]=1; } ans=kp(n*k); cout<<ans.a[r][0]; return 0; }
by:s_a_b_e_r
以上是关于六省联考:组合数问题的主要内容,如果未能解决你的问题,请参考以下文章