bzoj P4870 [Shoi2017]组合数问题

15 篇文章 0 订阅
1 篇文章 0 订阅

传送门

炒鸡强的dp啊,原来组合题还可以这么做,用组合的意义来dp,只做过用组合优化dp。

我们考虑一下那个式子的意义:

nk个东西中取r个的方案数+nk个东西取k+r个的方案数+nk个东西取2k+r个的方案数+...

然后进一步转化问题:从nk个物品里选取膜k余r个物品的方案数。

那么怎么求这个问题呢?

很容易能想到一个简单的dp,设f[i][j]表示前i个物品取了膜k余j的方案数,转移很简单,f[i][j]=f[i-1][j]+f[i-1][(j+k-1)%k],一个取i,一个不取i。

然后我们发现这个式子只与f[i-1]有关,那么直接上矩阵快速幂。

代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#define ll long long
using namespace std;
const int Maxn=55;
int p,k,r;
ll n;
struct _Matrix{ll Matrix[Maxn][Maxn];_Matrix(){for(int i=0;i<=53;i++)for(int j=0;j<=53;j++)Matrix[i][j]=0;}}f,m;
_Matrix operator * (_Matrix a,_Matrix b)
{
	_Matrix c;
	for(int i=0;i<k;i++)
		for(int j=0;j<k;j++)
			for(int l=0;l<k;l++)
				c.Matrix[i][j]=(c.Matrix[i][j]%p+(a.Matrix[i][l]%p*b.Matrix[l][j]%p)%p)%p;
	return c; 
}
_Matrix operator ^ (_Matrix a,ll b)
{
	_Matrix c;
	for(int i=0;i<k;i++)
		c.Matrix[i][i]=1;
	while(b)
	{
		if(b&1)c=c*a;
		a=a*a;b>>=1;
	}
	return c;
} 
int main()
{
	scanf("%lld%d%d%d",&n,&p,&k,&r);
	for(int i=0;i<k;i++)
		m.Matrix[(i-1+k)%k][i]++,m.Matrix[i][i]++;
	n*=k;
	m=m^n;
	f.Matrix[0][0]=1;
	f=f*m;
	printf("%lld",f.Matrix[0][r]%p);	
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值