题目:
题意:
有
m
m
m种物品,数量无限,用
(
a
i
,
b
i
,
x
i
)
(a_i,b_i,x_i)
(ai,bi,xi)三元组来形容物品,
x
i
x_i
xi表示我们选出该种物品多少个
对答案的贡献为
∏
i
=
1
n
1
+
a
i
x
i
2
+
b
i
x
i
\prod_{i=1}^n1+a_ix_i^2+b_ix_i
∏i=1n1+aixi2+bixi
求所有方案的贡献和
分析:
设
f
i
,
j
f_{i,j}
fi,j表示考虑到第
i
i
i位已经考虑完前
j
j
j种物品
会有
f
i
,
j
=
∑
k
=
0
i
f
i
−
k
,
j
−
1
∗
(
a
j
k
2
+
b
j
k
+
1
)
f_{i,j}=\sum_{k=0}^if_{i-k,j-1}*(a_jk^2+b_jk+1)
fi,j=∑k=0ifi−k,j−1∗(ajk2+bjk+1)
此时复杂度为
O
(
n
2
m
)
O(n^2m)
O(n2m)
我们不妨将方程逆过来推
f
i
,
j
∗
(
a
j
k
2
+
b
j
k
+
1
)
→
f
i
+
k
,
j
+
1
f_{i,j}*(a_jk^2+b_jk+1)→f_{i+k,j+1}
fi,j∗(ajk2+bjk+1)→fi+k,j+1
式子的左边展开得
f
i
,
j
∗
a
j
k
2
+
f
i
,
j
∗
b
j
k
+
f
i
,
j
f_{i,j}*a_jk^2+f_{i,j}*b_jk+f_{i,j}
fi,j∗ajk2+fi,j∗bjk+fi,j
我们考虑将三个单项式分开求出最后合并
阔以运用前缀和的芝士,将
k
2
,
k
,
1
k^2,k,1
k2,k,1分别用三遍,两遍,一遍前缀和来代替这个系数
其他照搬式子
复杂度就来到优秀的
O
(
n
m
)
O(nm)
O(nm)了
当然还有更秀的生成函数,我不会,爬了
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#define LL long long
#define mo 998244353
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
LL a[1005],b[1005];
LL sum1[10005],sum2[10005],sum3[10005],f[10005][1005];
int main()
{
LL m=read();
for(LL i=1;i<=m;i++) a[i]=read(),b[i]=read();
f[0][0]=1;
for(LL j=0;j<m;j++)
{
memset(sum1,0,sizeof(sum1));
memset(sum2,0,sizeof(sum2));
memset(sum3,0,sizeof(sum3));
for(LL i=0;i<=10000;i++)
{
(sum1[i]+=f[i][j])%=mo;
(sum2[i+1]+=f[i][j]*b[j+1]%mo)%=mo;
(sum3[i+1]+=f[i][j]%mo*a[j+1]%mo)%=mo;
(sum3[i+2]+=f[i][j]%mo*a[j+1]%mo)%=mo;
}
for(LL i=1;i<=10000;i++) (sum1[i]+=sum1[i-1])%=mo;
for(LL i=1;i<=10000;i++) (sum2[i]+=sum2[i-1])%=mo;
for(LL i=1;i<=10000;i++) (sum2[i]+=sum2[i-1])%=mo;
for(LL i=1;i<=10000;i++) (sum3[i]+=sum3[i-1])%=mo;
for(LL i=1;i<=10000;i++) (sum3[i]+=sum3[i-1])%=mo;
for(LL i=1;i<=10000;i++) (sum3[i]+=sum3[i-1])%=mo;
for(LL i=0;i<=10000;i++) f[i][j+1]=(sum1[i]+sum2[i]+sum3[i])%mo;
}
LL q=read();
while(q--) printf("%lld\n",f[read()][m]);
return 0;
}