博客园 首页 私信博主 显示目录 隐藏目录 管理

六省联考:组合数问题

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;
}
s_a_b_e_r

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;
}
4870(wypx)
by:wypx

 

s:评测机又卡了
w:我连自动售货机都能卡x
 
 
posted @ 2017-09-03 21:15  ck666  阅读(248)  评论(0编辑  收藏  举报