zoukankan      html  css  js  c++  java
  • [六省联考2017] 组合数问题

    一、题目

    点此看题

    二、解法

    其实不一定是递推问题才想矩阵加速,比如某个东西很大,但是某个东西很小的时候就可以尝试矩阵乘法了。

    这道题就用很小的量来定义状态就行了,设 (f(i,j)) 表示考虑了 (i) 个数,选的总数模 (k)(j),那么每次就考虑这个数选还是不选,不难写出转移(第二维在模意义下):

    [f(i,j)=f(i-1,j)+f(i-1,j-1) ]

    这个就很容易矩阵乘法了,时间复杂度 (O(k^3log(nk))),注意考虑下 (k=1) 的情况。


    还有一个做法也是利用了快速幂去算这东西,只不过用的是多项式的快速幂。那么如果我们要去套多项式快速幂怎么办呢?这就要求我们找到沟通组合数和多项式的桥梁了,这里可以用二项式定理:

    [egin{aligned} &=sum_{imod k=r}{nkchoose i}\ &=sum_{imod k=r}[x^i](1+x)^{nk}\ &=[x^r]((1+x)^{nk}mod (x^k-1)) end{aligned} ]

    直接循环卷积快速幂,时间复杂度 (O(k^2log (nk)))不会真的有人无聊到去写多项式乘法吧

    #include <cstdio>
    const int M = 1005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,p,k,r,a[M],b[M],A[M],B[M];
    void mul(int *a,int *b)
    {
    	for(int i=0;i<k;i++) A[i]=a[i],B[i]=b[i],b[i]=0;
    	for(int i=0;i<k;i++)
    		for(int j=0;j<k;j++)
    			b[(i+j)%k]=(b[(i+j)%k]+A[i]*B[j])%p;
    }
    signed main()
    {
    	n=read();p=read();k=read();r=read();
    	if(k==1) a[0]=2%p;//这时候都在常数项
    	else a[0]=a[1]=1;
    	n*=k;b[0]=1;
    	while(n>0)
    	{
    		if(n&1) mul(a,b);
    		mul(a,a);
    		n>>=1;
    	}
    	printf("%lld
    ",b[r]);
    }
    
  • 相关阅读:
    设计模式学习总结
    算法时间复杂度和空间复杂度表示
    SQLite简单使用
    接口,组合和继承的想法
    二叉树的学习
    Oracle 常用命令大汇总
    Oracle 最常用功能函数经典汇总
    oracle 常用command
    历史最牛演讲:Oracle总裁Yale演讲全文中英文对照
    深入abstract class和interface
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14599116.html
Copyright © 2011-2022 走看看