CF621E - Wet Shark and Blocks

本文介绍了一种使用动态规划(DP)算法解决组合数学问题的方法,具体涉及到从多个元素集合中选择元素并组成特定模式的过程。通过构建状态转移方程和矩阵运算,有效地解决了求解特定条件下方案数量的问题。

题目大意:给你b个格子,每个格子有n个数(1到9),你顺序从b个格子中各取一个数,依次排列好,组成一个b位数,求使得这个b位数%x余k的方案数(同一格子内相同的数字算不同方案)。

DP题,状态f[i][j]表示前i个格子取出的数%x余j的方案数。f[i][j]到f[i+1][m]转移条件是(j*10+q)%x==m,q为第i+1个格子选的数。化简之后变成F[i]*A[]=F[i+1],F和A为矩阵,这个题就变成了矩阵乘法,然后由于b太大,用快速幂优化,最后输出余k的答案即可。

难点在于A矩阵的构造,想不通的可以看一下我代码。。。虽然丑但是可读性应该是还阔以的。

快速幂一定要写非递归的。。。不然一万的long long会爆栈。。。mod注意点就好。

第一次做出来e题,也是有点小兴奋。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
int n,b,k,x;
int a[55000];
int bb[110];
long long modd;
struct MATRIX{
	long long m[110][110];
	MATRIX friend operator*(MATRIX a,MATRIX b){
		MATRIX c;
		memset(c.m,0,sizeof(c));
		for(int i=0;i<x;i++){
			for(int j=0;j<x;j++){
				for(int k=0;k<x;k++){
					c.m[i][j]+=(a.m[i][k]*b.m[k][j]);
					c.m[i][j]%=modd;
				}
			}
		}
		return c;
	}
}q,ans,c,dui[110];
void PR(MATRIX a){
	printf("\n");
	for(int i=0;i<x;i++){
		for(int j=0;j<x;j++)printf("%d ",a.m[i][j]);
		printf("\n");
	}
	printf("\n");
}
int main(){
	modd=1000000000+7;
	scanf("%d%d%d%d",&n,&b,&k,&x);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)bb[a[i]%x]++;
	for(int i=0;i<x;i++){
		for(int j=0;j<x;j++){
			q.m[i][(i*10+j)%x]=bb[j%x];
			q.m[i][(i*10+j)%x]%=modd;
		}
	}
//	PR(q);
//	q=KUAISUMI(q,b-1);
	int tail=1;
	int w=b-1;
	dui[1]=q;
	while(w!=1&&w!=0){
		if(w&1){
			tail++;
			dui[tail]=dui[tail-1]*dui[tail-1];
		}else{
			dui[tail]=dui[tail]*dui[tail];
		}
		w>>=1;
	}
	while(tail!=1){
		dui[tail-1]=dui[tail-1]*dui[tail];
		tail--;
	}
	q=dui[1];
	for(int i=0;i<x;i++)ans.m[0][i]=bb[i];
//	PR(ans);
	if(b!=1)ans=ans*q;
	cout<<ans.m[0][k]<<endl;
	return 0;
}
//f[i][j][k] 前i个箱子中,第i个箱子拿第j个数余k的数量 
//j<=1..n f[i][k*10+a[j]]+=f[i-1][k]
//f[i][0..x-1]=f[i-1][0..x-1]*A[0..x-1][0..x-1]


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值