nssl 1519.背包签到题

D e s c r i p t i o n Description Description

n n n个物品,每个物品有 a i a_i ai件,要选择 k k k轮,每次可以选任意件物品,求本质不同的方案数

数据范围:
nssl 1519.背包签到题_第1张图片


S o l u t i o n Solution Solution

30 p t s 30pts 30pts

考虑当 n = 1 n=1 n=1时怎么做,我们把最后一位定义为第一位,以此类推

f i , j f_{i,j} fi,j表示当前进行到第 i i i位,可供选择的数字有 j j j个,令 s = ∑ a i + 1 s=\sum a_i+1 s=ai+1,则有

一位: f 1 , s = s f_{1,s}=s f1,s=s
二位: f 2 , s = ∑ i = 1 s i f_{2,s}=\sum _{i=1}^{s} i f2,s=i=1si
三位: f 3 , s = ∑ j = 1 s ∑ i = 1 j i f_{3,s}=\sum_{j=1}^s\sum _{i=1}^j i f3,s=j=1si=1ji
四位: f 4 , s = ∑ k = 1 s ∑ j = 1 k ∑ i = 1 j i f_{4,s}=\sum _{k=1}^s \sum_{j=1}^k \sum_{i=1}^j i f4,s=k=1sj=1ki=1ji

得到递推式
i i i位: f i , j = ∑ l = 1 j f i − 1 , l f_{i,j}=\sum_{l=1}^j f_{i-1,l} fi,j=l=1jfi1,l

这样子是 O ( k s 2 ) O(ks^2) O(ks2)的,考虑看一下 f i , j f_{i,j} fi,j f i , j − 1 f_{i,j-1} fi,j1的关系,得到了
f i , j = f i , j − 1 + f i − 1 , j f_{i,j}=f_{i,j-1}+f_{i-1,j} fi,j=fi,j1+fi1,j
O ( k s ) O(ks) O(ks)了,还是30分


50 p t s 50pts 50pts

基于 30 p t s 30pts 30pts

发现这个式子很像前缀和的形式,于是我们令 p i , j = ∑ k = 1 j f i , k p_{i,j}=\sum _{k=1}^j f_{i,k} pi,j=k=1jfi,k

p i , j = p i , j − 1 + f i , j p_{i,j}=p_{i,j-1}+f_{i,j} pi,j=pi,j1+fi,j
f i , j = p i − 1 , j f_{i,j}=p_{i-1,j} fi,j=pi1,j

我们把 f f f接到 p p p的后面,然后做矩阵乘法,可以得到 50 p t s 50pts 50pts【实测只有30,野鸡OJ太垃圾了】


100 p t s 100pts 100pts

基于 30 p t s 30pts 30pts
注意到 f f f的方程相当于可以从上边和左边转移,也就是组合数 C k + a i a i C_{k+a_i}^{a_i} Ck+aiai(每一步可以向上或向左边揍)

然后又发现 a i a_i ai间互不干扰,直接乘起来即可

时间复杂度: O ( n a i ) O(na_i) O(nai)(可以通过预处理做到 O ( n + a i ) O(n+a_i) O(n+ai)


C o d e Code Code

#include
#include
#include
#include
#define LL long long
#define mod 998244353
using namespace std;int n,s;
LL fac[110],inv[110],k,Ans=1,a[100011];
inline LL read()
{
     
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline LL ksm(LL x,LL y)
{
     
	LL res=1;
	for(;y;y>>=1,(x*=x)%=mod) if(y&1) (res*=x)%=mod;
	return res;
}
signed main()
{
     
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for(register int i=2;i<=100;i++) fac[i]=fac[i-1]*i%mod;
	inv[100]=ksm(fac[100],mod-2);
	for(register int i=99;i>1;i--) inv[i]=(1ll*inv[i+1]*(i+1))%mod;
	n=read();k=read();
	for(register int i=1;i<=n;i++) 
	{
     	
		a[i]=read();
		for(register int j=0;j<a[i];j++) (Ans*=(k+a[i]-j+mod)%mod)%=mod;
		(Ans*=inv[a[i]])%=mod;
	}
	printf("%lld",Ans);
}

你可能感兴趣的:(nssl,1519,背包签到题)