zoukankan      html  css  js  c++  java
  • [BZOJ 4870] 组合数问题

    Link:

    传送门

    Solution:

    组合数的式子都可以先想想能不能递推,写出来就是:

    $sum C_{n*k}^{i*k+r}=sum C_{n*k-1}^{i*k+r}+sum C_{n*k-1}^{i*k+r-1}$

    如果将每个求和看成一个整体,设$dp[n][r]=sum C_{n}^{i*k+r}$,

    则有$dp[n][r]=dp[n-1][r]+dp[n-1][(r-1+k)modk]$

    由于$r$就相当于余数因此0-1后要变为$k-1$!

    这样的递推式明显可以矩乘,直接上的话就是:

    $新列向量=n*n矩阵 imes 原列向量$,第$i$行将$s[i][i],s[i][(i-1+k)modk]$置1即可

    不过注意这是一个循环矩阵,那么其实只要计算第一列,其他列都是其转动的结果

    对于某一列有贡献的只有$n^2$个乘积,如果将每一对都转化成第一列的坐标发现是:

    $s[k]=sum_i sum_j [(i+j)modn==k]s[i]*s[j]$ (下标从0开始) 

    而之所以$答案列向量 imes 第一列$也是这个式子感觉要从算贡献来考虑,可能是个巧合?

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=55;
    int n,p,r,k,res[MAXN],a[MAXN],t[MAXN];
    
    void mul(int *x,int *y)
    {
        memset(t,0,sizeof(t));
        for(int i=0;i<=k;i++)
            for(int j=0;j<=k;j++)
                (t[(i+j)%k]+=1ll*x[i]*y[j]%p)%=p;
        for(int i=0;i<=k;i++) x[i]=t[i];
    }
    
    int main()
    {
        scanf("%d%d%d%d",&n,&p,&k,&r);
        res[0]=1;a[0]++;a[1%k]++;
        
        for(ll idx=1ll*n*k;idx;idx>>=1,mul(a,a))
            if(idx&1) mul(res,a);
        printf("%d",res[r]);
        return 0;
    }
  • 相关阅读:
    [BZOJ1303][CQOI2009]中位数图
    [BZOJ1192][HNOI2006]鬼谷子的钱袋
    9.5题解
    9.3题解
    9.2题解
    9.1题解
    8.29题解
    8.28题解
    8.23<2>题解
    8.23<1>题解
  • 原文地址:https://www.cnblogs.com/newera/p/10009410.html
Copyright © 2011-2022 走看看