文章目录
R e s u l t Result Result
H y p e r l i n k Hyperlink Hyperlink
https://ac.nowcoder.com/acm/contest/186/D
D e s c r i p t i o n Description Description
一个无限手套,有
n
n
n个槽,第
i
i
i个槽有属性
a
i
,
b
i
a_i,b_i
ai,bi
若手套第
i
i
i个槽装上了
x
x
x颗宝石,这个槽会产生
(
a
i
x
2
+
b
i
x
+
1
)
(a_ix^2+b_ix+1)
(aix2+bix+1)的能量
无限手套的能量是它每个槽上面的能量的积
q
q
q个询问,每个询问给定一个
m
m
m,询问如果有
m
m
m个宝石,所有情况产生的能量的和是多少?
答案对998244353取模
数据范围: n ≤ 1 0 3 , m ≤ 1 0 4 n\leq 10^3,m\leq 10^4 n≤103,m≤104
S o l u t i o n Solution Solution
据说有生成函数的做法,但是我不会【我太菜了】
设
f
i
,
j
f_{i,j}
fi,j表示处理到第
i
i
i个槽,
j
j
j颗宝石能产生的能量之和
则我们显然是枚举上一个槽选了多少个宝石
k
k
k,有转移
f
i
,
j
=
∑
k
=
0
j
f
i
−
1
,
j
×
y
(
j
−
k
)
f_{i,j}=\sum_{k=0}^j f_{i-1,j}\times y(j-k)
fi,j=∑k=0jfi−1,j×y(j−k),
y
(
x
)
y(x)
y(x)表示在第
i
i
i个槽下装
x
x
x个宝石会产生的能量
直接这样做时间复杂度为 O ( n m 2 ) O(nm^2) O(nm2)显然会 T T T,我们考虑优化它
观察
f
i
,
j
f_{i,j}
fi,j的式子可以发现,实际上它和上一次枚举的
f
i
,
j
−
1
f_{i,j-1}
fi,j−1只有两个地方不同,我们写出来看一下
f
i
,
j
=
∑
k
=
0
j
f
i
−
1
,
j
×
y
(
j
−
k
)
f_{i,j}=\sum_{k=0}^j f_{i-1,j}\times y(j-k)
fi,j=∑k=0jfi−1,j×y(j−k)
f
i
,
j
−
1
=
∑
k
=
0
j
−
1
f
i
−
1
,
j
×
y
(
j
−
k
−
1
)
f_{i,j-1}=\sum_{k=0}^{j-1} f_{i-1,j}\times y(j-k-1)
fi,j−1=∑k=0j−1fi−1,j×y(j−k−1)
f
i
,
j
f_{i,j}
fi,j在求和相差的那一部分多枚举了一个
k
=
j
k=j
k=j的情况
这种情况的差值显然就是
A
=
f
i
−
1
,
j
×
y
(
j
−
k
)
=
f
i
−
1
,
j
A=f_{i-1,j}\times y(j-k)=f_{i-1,j}
A=fi−1,j×y(j−k)=fi−1,j
考虑计算求和中间那一段的差值,试着上下相减,看一下每一项的差值
f
i
−
1
,
j
×
y
(
j
−
k
)
−
f
i
−
1
,
j
×
y
(
j
−
k
−
1
)
f_{i-1,j}\times y(j-k) -f_{i-1,j}\times y(j-k-1)
fi−1,j×y(j−k)−fi−1,j×y(j−k−1)
分配率
f
i
−
1
,
j
×
[
y
(
j
−
k
)
−
y
(
j
−
k
−
1
)
]
f_{i-1,j}\times[y(j-k)-y(j-k-1)]
fi−1,j×[y(j−k)−y(j−k−1)]
把
y
y
y拆开
f
i
−
1
,
j
×
[
a
i
(
j
−
k
)
2
+
b
i
(
j
−
k
)
−
a
i
(
j
−
k
−
1
)
2
−
b
i
(
j
−
k
−
1
)
]
f_{i-1,j}\times[a_i(j-k)^2+b_i(j-k)-a_i(j-k-1)^2-b_i(j-k-1)]
fi−1,j×[ai(j−k)2+bi(j−k)−ai(j−k−1)2−bi(j−k−1)](常数项显然抵消掉了)
由
x
2
−
(
x
−
1
)
2
=
2
x
−
1
x^2-(x-1)^2=2x-1
x2−(x−1)2=2x−1,可以得到
f i − 1 , j × [ a i ( 2 j − 2 k − 1 ) + b i ] f_{i-1,j}\times[a_i(2j-2k-1)+b_i] fi−1,j×[ai(2j−2k−1)+bi]
考虑把与
j
j
j相关的写一起,与
k
k
k相关的写一起
得到了第二部分的差值
B
=
f
i
−
1
,
j
×
[
b
i
+
(
2
j
−
1
)
a
i
−
2
k
a
i
]
B=f_{i-1,j}\times[b_i+(2j-1)a_i-2ka_i]
B=fi−1,j×[bi+(2j−1)ai−2kai]
b
i
,
a
i
b_i,a_i
bi,ai是常量,换言之这些是我们已知的
也就是每一个
f
i
,
j
f_{i,j}
fi,j都和
f
i
,
j
−
1
f_{i,j-1}
fi,j−1差了
A
+
B
A+B
A+B
A A A显然很好求,考虑求 B B B
对于每一个 k k k,都会有 f i − 1 , j [ b i + ( 2 j − 1 ) a i − 2 k a i ] f_{i-1,j}[b_i+(2j-1)a_i-2ka_i] fi−1,j[bi+(2j−1)ai−2kai]的差值
观察到我们始终只关心
f
i
f_i
fi和
f
i
−
1
f_i-1
fi−1,为了方便我们把它们分别设成
f
f
f和
g
g
g【这次的和上一次的】
则
B
=
g
j
[
b
i
+
(
2
j
−
1
)
a
i
−
2
k
a
i
]
B=g_j[b_i+(2j-1)a_i-2ka_i]
B=gj[bi+(2j−1)ai−2kai]
拆成两坨
B
=
g
j
[
b
i
+
(
2
j
−
1
)
a
i
]
−
g
i
×
2
k
a
i
B=g_j[b_i+(2j-1)a_i]-g_i\times 2ka_i
B=gj[bi+(2j−1)ai]−gi×2kai
记录前缀和
s
j
=
∑
k
=
0
j
g
j
,
S
k
=
∑
j
=
0
k
g
j
×
2
j
s_j=\sum_{k=0}^j g_j,S_k=\sum_{j=0}^k g_j\times 2j
sj=∑k=0jgj,Sk=∑j=0kgj×2j
这样就可以从 f j − 1 f_{j-1} fj−1快速的转移到 f j f_j fj了,因为 f j f_j fj和 f j − 1 f_{j-1} fj−1的差值就是 A + B A+B A+B
即
f
j
=
f
j
−
1
+
A
+
B
f_j=f_{j-1}+A+B
fj=fj−1+A+B,这是笼统的总式子,接下来我们把它写细一点
首先
A
A
A是以前的
f
i
−
1
,
j
f_{i-1,j}
fi−1,j,现在换了
d
p
dp
dp后是
g
j
g_j
gj
B
B
B可以表示成两段前缀和,即
s
j
−
1
×
(
b
i
+
(
2
j
−
1
)
a
i
)
−
S
j
−
1
×
a
i
s_{j-1}\times (b_i+(2j-1)a_i)-S_{j-1}\times a_i
sj−1×(bi+(2j−1)ai)−Sj−1×ai【之所以是
j
−
1
j-1
j−1是因为
f
j
−
1
f_j-1
fj−1只计算到
j
−
1
j-1
j−1,所以这一部分的差值应该是到
j
−
1
j-1
j−1】
写成真正的
d
p
dp
dp转移方程
f
j
=
f
j
−
1
+
g
j
+
s
j
−
1
×
(
b
i
+
(
2
j
−
1
)
a
i
)
−
S
j
−
1
×
a
i
f_j=f_{j-1}+g_j+s_{j-1}\times (b_i+(2j-1)a_i)-S_{j-1}\times a_i
fj=fj−1+gj+sj−1×(bi+(2j−1)ai)−Sj−1×ai【前面有一个
f
j
−
1
f_{j-1}
fj−1是因为我们后面算的是和上一次的差值】
有点丑,将常量
b
i
+
(
2
j
−
1
)
a
i
b_i+(2j-1)a_i
bi+(2j−1)ai用变量
t
m
p
tmp
tmp表示起来,就舒服多了
f j = f j − 1 + g j + s j − 1 × t m p − S j − 1 × a i f_j=f_{j-1}+g_j+s_{j-1}\times tmp-S_{j-1}\times a_i fj=fj−1+gj+sj−1×tmp−Sj−1×ai
最后答案就是 f m f_m fm,时间复杂度: O ( n m ) O(nm) O(nm)
C o d e Code Code
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 10001
#define mod 998244353
using namespace std;int n,q;
LL a,b,f[N],g[N],s[N],S[N],tmp;
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;
}
signed main()
{
f[0]=g[0]=s[0]=1;n=read();
for(register int i=1;i<=n;i++)
{
a=read();b=read();
for(register int j=1;j<N;j++) (s[j]=s[j-1]+g[j])%=mod,(S[j]=S[j-1]+2*g[j]*j%mod)%=mod;
for(register int j=1;j<N;j++) tmp=(b+(2*j-1)*a%mod)%mod,f[j]=(f[j-1]+g[j]+(s[j-1]*tmp%mod-S[j-1]*a%mod+mod)%mod)%mod;
for(register int j=1;j<N;j++) g[j]=f[j];
}
q=read();
while(q--) printf("%lld\n",f[read()]);
}