六省联考:组合数问题
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
表示当时省选划水的时候还是一只只会模拟的萌新qwq
这个题乍一看就是一数学题,然而推半天公式只能得到完全不可做的结论qwq
事实上这题跟组合数学基本没啥关系
组合数一开始的定义就是在n个数中取m个数的方案数
此处可有递推方程(f[i][j]=f[i-1][j]+f[i-1][j-1])
这题稍微变了一下条件
在i个数中取x个数使得x%k==j
于是有递推方程f[i][j]=f[i-1][j]+f[i-1][(j-1+k)%k]
然而数据范围O(n*k)==TLE。
需要加一个优化
因为楼上说的优化我都不会,于是去学矩阵加速
按照递推方程构造出矩阵
然后就可以矩阵快速幂解决了
是不是很快x
#include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; ll n,p,k,r; struct matrix{ ll a[55][55]; }ans,m; matrix operator * (matrix x,matrix y) { matrix s; memset(s.a,0,sizeof(s.a)); for(int i=0;i<k;++i) for(int j=0;j<k;++j) for(int l=0;l<k;++l) s.a[i][j]=(s.a[i][j]+x.a[i][l]*y.a[l][j])%p; if (k==1) s.a[0][0]=(s.a[0][0]+x.a[0][0]*y.a[0][0])%p; return s; } matrix kp(ll q) { matrix s; memset(s.a,0,sizeof(s.a)); for(int i=0;i<k;++i)s.a[i][i]=1; while(q) { if(q&1)s=s*m; m=m*m; q>>=1; } return s; } int main() { cin>>n>>p>>k>>r; for(int i=0;i<k;++i) { m.a[i][i]=1; m.a[i][(i-1+k)%k]=1; } matrix ans=kp(n*k); printf("%lld",ans.a[0][r]); return 0; }
s:评测机又卡了
w:我连自动售货机都能卡x