组合数问题
Time Limit: 10 Sec Memory Limit: 512 MB
[Submit][Status][Discuss]
Description
Input
第一行有四个整数 n, p, k, r,所有整数含义见问题描述。
Output
一行一个整数代表答案。
Sample Input
2 10007 2 0
Sample Output
8
HINT
1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1
Solution
首先,不难发现,题目的本质是:从n*k个中选模k等于r个的方案数,那么轻易地写出了暴力DP:f[i][j]=f[i-1][j]+f[i-1][(j-1+k)%k]。
然后套个矩阵乘法优化一下即可。
Code
#include<iostream> #include<string> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> using namespace std; typedef long long s64; const int ONE = 55; int n,MOD,num,r; inline s64 get() { s64 res=1,Q=1; char c; while( (c=getchar())<48 || c>57) if(c==‘-‘)Q=-1; if(Q) res=c-48; while((c=getchar())>=48 && c<=57) res=res*10+c-48; return res*Q; } struct Matrix { s64 v[ONE][ONE]; friend Matrix operator *(Matrix a,Matrix b) { Matrix record; for(int i=0;i<num;i++) for(int j=0;j<num;j++) { record.v[i][j]=0; for(int k=0;k<num;k++) record.v[i][j] = (s64)(record.v[i][j] + a.v[i][k]*b.v[k][j] % MOD) % MOD; } return record; } }; Matrix B,Ans; Matrix Quickpow(Matrix a,s64 b) { Matrix res; for(int i=0;i<num;i++) res.v[i][i] = 1; while(b) { if(b&1) res = res*a; a = a*a; b>>=1; } return res; } int main() { n=get(); MOD=get(); num=get(); r=get(); for(int i=0;i<num;i++) { B.v[i][i]++; B.v[((i-1)%num+num)%num][i]++; } Ans = Quickpow(B, (s64)n*num); cout<<Ans.v[0][r]; }
时间: 2024-11-04 06:42:15