思路:一个很容易想的DP
设 f i , j f_{i,j} fi,j 表示在前 i i i 个数中取 j j j 个所得到的乘积。
则
f
i
,
j
=
f
i
−
1
,
j
+
f
i
−
1
,
j
−
1
×
a
i
(
i
>
0
,
j
>
0
)
f_{i,j}=f_{i-1,j}+f_{i-1,j-1}×a_i \quad(i>0,j>0)
fi,j=fi−1,j+fi−1,j−1×ai(i>0,j>0)
边界:
f
0
,
0
=
1
f
0
,
i
=
0
(
i
≠
0
)
f
i
,
0
=
0
(
i
≠
0
)
f_{0,0}=1\quad f_{0,i}=0\ (i\ne0) \quad f_{i,0}=0\ (i\ne 0)
f0,0=1f0,i=0 (i̸=0)fi,0=0 (i̸=0)
但是直接DP会爆空间。。。
观察到DP时只需调用到
f
i
−
1
f_{i-1}
fi−1 ,所以可以用滚动数组的方式优化空间。
代码时间复杂度 O ( n k ) \mathcal O(nk) O(nk),空间复杂度 O ( n + k ) \mathcal O(n+k) O(n+k)
const
mo=1000000007; //膜数
var
n,k,i,j:longint;
a:array[0..100010] of longint;
f1,f2:array[0..500] of int64; //安全起见开int64
begin
readln(n,k);
for i:=1 to n do read(a[i]);
f1[0]:=1; //边界初始化
for i:=1 to n do //DP
begin
for j:=0 to k do
f2[j]:=(f1[j]+f1[j-1]*a[i]) mod mo;
f1:=f2;
end;
writeln(f1[k]);
end.
做完题后仔细想了一下,发现
a
\rm a
a 数组的空间也是可以省掉的,在线处理就可以,不用存。
然后就有了空间复杂度 $\mathcal O(k) $ 的代码:
const mo=1000000007;
var
n,k,i,j,a:longint;
f1,f2:array[0..500] of int64;
begin
readln(n,k);
f1[0]:=1;
for i:=1 to n do
begin
read(a); //这样就可以把a压成变量了QAQ
for j:=0 to k do
f2[j]:=(f1[j]+f1[j-1]*a) mod mo;
f1:=f2;
end;
writeln(f1[k]);
end.