卧薪尝胆,厚积薄发。
自古枪兵幸运E
Date: Thu Feb 28 21:03:30 CST 2019
In Category:
NoCategory
Description:
有
$n$
种大小为
$1$
的物品和有
$m$
种大小为
$2$
的物品,每种都有无限个,求填满大小为
$k$
的背包的方案数
$\bmod P$
。
$1\leqslant n,m\leqslant 10^5,1\leqslant k\leqslant 10^{12}$
Solution:
最后答案的生成函数为
$(\frac1{1-x})^n(\frac1{1-x^2})^m$
,我们乘上
$(\frac1{1+x})^n(1+x)^n$
,那么答案就是
$(\frac1{1-x^2})^{n+m}(1+x)^n$
,然后我们可以枚举右边那一项的次数为
$i\in[0,n]$
,他的系数就是
$\binom n i$
,然后左边就是
$n+m$
个物品,每个物品体积为
$2$
,要求装满
$k-i$
的背包的方案数,也就是把
$\frac{k-i}2$
划分为可空的
$n+m$
的集合的问题,显然可以用隔板法,模数不大用卢卡斯定理求组合数。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
#define MAXP 1000000
typedef long long ll;
int n,m,MOD;
ll k;
int fac[MAXP + 5],inv[MAXP + 5];
int power(int a,int b){int r = 1;while(b > 0){if(b & 1)r = 1ll * r * a % MOD;a = 1ll * a * a % MOD,b = b >> 1;}return r;}
void init()
{
fac[0] = 1;
for(int i = 1;i < MOD;++i)fac[i] = 1ll * fac[i - 1] * i % MOD;
inv[MOD - 1] = power(fac[MOD - 1],MOD - 2);
for(int i = MOD - 2;i >= 0;--i)inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
return;
}
int C(int n,int m)
{
if(n < m)return 0;
return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
int Lucas(ll n,ll m)
{
if(n < m)return 0;
if(n < MOD)return C(n,m);
else return 1ll * Lucas(n / MOD,m / MOD) * C(n % MOD,m % MOD) % MOD;
}
int divide(ll n,ll m)
{
return Lucas(n + m - 1,m - 1);
}
int query(int n,int m,ll k,int MOD)
{
int ans = 0;
for(int i = 0;i <= n;++i)
{
if((k - i) % 2 == 1)continue;
ans = (ans + 1ll * Lucas(n,i) * divide((k - i) / 2,n + m) % MOD) % MOD;
}
return ans;
}
void work()
{
scanf("%d%d%lld%d",&n,&m,&k,&MOD);
init();
printf("%d\n",query(n,m,k,MOD));
return;
}
int main()
{
int testcases;
scanf("%d",&testcases);
while(testcases--)work();
return 0;
}
In tag:
数学-组合数学-生成函数 数学-组合数学-卢卡斯定理
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡