• 题解 lg3746 组合数问题


    题面

    式子看起来很吓人哈

    不过我们来考虑它的实际意义

    就是在 nk 个物品中选择 模k余数为r 的方案数和

    (f[i,j]) 表示选 在 i 个物品中选择 模k余数为j 的方案数和

    容易得到 (f[i,j]=f[i-1,j-1]+f[i-1,j])

    当然,为了避免负数,我们写成这种模样 (f[i,j]=f[i-1,(j+k-1)\%k]+f[i-1,j])

    这种形式可以写成矩阵

    然后就矩阵快速幂 啦啦啦啦


    当然,我们可以继续优化

    发现这个式子也可以不由 i-1转移过来,而是由两个状态合并而来

    (f[x+y,k]=f[x,0]f[y,k]+f[x,1]f[y,k-1]...f[x,k]f[y,0])

    然后可以dp快速幂了


    然后说如果p特殊,可以用单位根反演?(%%%boshi)


    就只上第一种做法的代码吧(准确来说我只会这一种)

    注意当k=1时,矩阵会退化为单个变量,用赋值的方法会挂;应该自加

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int const MAXN=60;
    int n,p,k,r;
    struct Mat{
    	int m[MAXN][MAXN];
    	Mat(){
    		memset(m,0,sizeof(m));
    	}
    	Mat operator*(const Mat& b) const{
    		Mat x;
    		for(int i=0;i<k;i++){
    			for(int l=0;l<k;l++){
    				int a=m[i][l];if(!a)continue;
    				for(int j=0;j<k;j++){
    					x.m[i][j]+=1ll*a*b.m[l][j]%p;
    					if(x.m[i][j]>=p)x.m[i][j]-=p;
    				}
    			}
    		}
    		return x;
    	}
    	void init(){
    		for(int i=0;i<k;i++)m[i][i]=1;
    	}
    	void print(){
    		for(int i=0;i<k;i++){
    			for(int j=0;j<k;j++){
    				printf("%d ",m[i][j]);
    			}
    			printf("
    ");
    		}
    	}
    }a,b;
    void mpow(Mat &x,int b){
    	Mat ans;ans.init();
    	while(b){
    		if(b&1)ans=x*ans;
    		x=x*x;
    		b>>=1;
    	}
    	x=ans;
    }
    signed main(){
    	scanf("%lld%lld%lld%lld",&n,&p,&k,&r);
    	a.m[0][0]=1;
    	for(int i=0;i<k;i++){
    		b.m[i][i]++;
    		b.m[i][(i+k-1)%k]++;
    	}
    	mpow(b,n*k);
    	a=b*a;
    	printf("%lld
    ",a.m[r][0]); 
    	return 0;
    }
    
  • 相关阅读:
    Oracle 安装安全补丁过程中出现的问题
    Oracle 设置日志模式
    Oracle 设置archivelog错误解决方案
    sum() over() 函数的使用
    C盘清理,移动node 依赖和缓存文件
    ol设置最佳可视范围和限制缩放
    flex弹性布局模式下文字超出显示省略号
    查看svn本地账户和密码
    flutter apk启动闪退问题
    More than one file was found with OS independent path 'lib/armeabi-v7a/libflutter.so'
  • 原文地址:https://www.cnblogs.com/fpjo/p/14425420.html
Copyright © 2020-2023  润新知