题意
有n个位置,每个位置上的数是1到n中的任意一个,显然总的方案有
nn
n
n
种。对于每一种方案,定义其价值为
w[max(a[1..k])]∗w[max(a[2..k+1])]∗...∗w[max(a[n−k+1,n])]
w
[
m
a
x
(
a
[
1..
k
]
)
]
∗
w
[
m
a
x
(
a
[
2..
k
+
1
]
)
]
∗
.
.
.
∗
w
[
m
a
x
(
a
[
n
−
k
+
1
,
n
]
)
]
。现在给定
n,k
n
,
k
和
w[1..n]
w
[
1..
n
]
,求所有方案的价值和。
k≤n≤400
k
≤
n
≤
400
分析
设
f[x,y]
f
[
x
,
y
]
表示长度为
x
x
的序列中,最大值不超过的所有填数方案的价值和。
一种填法是不填
y
y
,那么。
否则我们可以枚举第一个
y
y
填哪里,设为位置
那么
f[x,y]=f[i−1,y−1]∗f[x−i,y]∗w[y]有多少个长度为k的区间包含位置i
f
[
x
,
y
]
=
f
[
i
−
1
,
y
−
1
]
∗
f
[
x
−
i
,
y
]
∗
w
[
y
]
有
多
少
个
长
度
为
k
的
区
间
包
含
位
置
i
用记忆化搜索来实现即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=405;
const int MOD=998244353;
int n,k,w[N][N],f[N][N];
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(LL)ans*x%MOD;
x=(LL)x*x%MOD;y>>=1;
}
return ans;
}
int dp(int x,int y)
{
if (!x) return 1;
if (x<k) return ksm(y,x);
if (!y) return 0;
if (f[x][y]) return f[x][y];
f[x][y]=dp(x,y-1);
for (int i=1;i<=x;i++)
{
int l=std::max(1,i-k+1),r=std::min(x,i+k-1)-k+1;
(f[x][y]+=(LL)w[y][r-l+1]*dp(i-1,y-1)%MOD*dp(x-i,y)%MOD)%=MOD;
}
return f[x][y];
}
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%d",&w[i][1]);
for (int i=1;i<=n;i++)
for (int j=2;j<=n;j++)
w[i][j]=(LL)w[i][j-1]*w[i][1]%MOD;
printf("%d",dp(n,n));
return 0;
}