Bzoj4870 [SXOI2017]组合数问题

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 155  Solved: 78

Description

Input

第一行有四个整数 n, p, k, r,所有整数含义见问题描述。
1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1

Output

一行一个整数代表答案。

Sample Input

2 10007 2 0

Sample Output

8

HINT

 

Source

数学问题 组合数

震惊!考场上花式骗分竟然可以拿到80分!

正解:

  这个东西当然没有什么既成的公式,需要用DP推公式,和平常的公式推DP正好反过来了。

  发现这个式子的项覆盖了nk内所有%k==r的位置,那么可以考虑这个式子的组合意义——

  在全部nk个物品中,选出任意个物品使得选出的物品数%k==r的方案数!

  设f[考虑到第i个物品][选出数量%k==j]=方案数,于是变成了可以$O(n^3)$推出来的背包问题?

  还可以更加简单粗暴,$f[2n][(i+j)%k]=sum f[n][i]*f[n][j] $ $O(n^2 logn)$倍增出解。

 1 /*by SilverN*/
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<cmath>
 7 #include<vector>
 8 #define LL long long
 9 using namespace std;
10 const int mxn=100010;
11 int read(){
12     int x=0,f=1;char ch=getchar();
13     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
14     while(ch>='0' && ch<='9'){x=x*10-'0'+ch;ch=getchar();}
15     return x*f;
16 }
17 int n,P,k,r;
18 struct num{
19     int x[52];
20     void init(){
21         memset(x,0,sizeof x);
22     }
23 }f;
24 num calc(const num &a,const num &b){
25     num res;
26     res.init();
27     for(int i=0;i<=k;i++)
28         for(int j=0;j<=k;j++)
29             (res.x[(i+j)%k]+=(LL)a.x[i]*b.x[j]%P)%=P;
30     return res;
31 }
32 num ksm(num a,LL t){
33     num res;
34     res.init();res.x[0]=1;
35     while(t){
36         if(t&1)res=calc(res,a);
37         a=calc(a,a);
38         t>>=1;
39     }
40     return res;
41 }
42 int main(){
43     int i,j;
44     n=read();P=read();k=read();r=read();
45     f.x[0]=1;
46     f.x[1%k]+=1;
47     f=ksm(f,(LL)n*k);
48 //    for(i=0;i<=k;i++)printf("%d
",f.x[i]);
49     printf("%d
",f.x[r]);
50     return 0;
51 }
原文地址:https://www.cnblogs.com/SilverNebula/p/6764493.html