题目大意: 有一个长度为 n n n 的序列 a a a,有一个 k k k,给出每个长度为 k k k 的子段的最小值,问这个序列有多少种可能性。
题解
容易发现假如 c i − 1 > c i c_{i-1}>c_i ci−1>ci,那么就可以确定 a i + k − 1 = c i a_{i+k-1}=c_i ai+k−1=ci。
所以需要考虑的是一段相同的 c i c_i ci 要如何处理,即考虑一个子问题:当所有 c i c_i ci 相等时,这个序列有多少种可能性?
设
f
i
f_i
fi 表示长度为
i
i
i 的序列的方案数,令
d
=
c
1
=
c
2
=
.
.
.
=
c
n
−
k
+
1
,
p
=
1
0
9
−
d
d=c_1=c_2=...=c_{n-k+1},p=10^9-d
d=c1=c2=...=cn−k+1,p=109−d,由于每
k
k
k 位至少有一个数恰好为
c
i
c_i
ci,可以得到这样的
dp
\text{dp}
dp 方程:
f
i
=
(
p
+
1
)
f
i
−
1
−
p
k
f
i
−
k
−
1
f_i=(p+1)f_{i-1}-p^kf_{i-k-1}
fi=(p+1)fi−1−pkfi−k−1
( p + 1 ) f i − 1 (p+1)f_{i-1} (p+1)fi−1 表示第 i i i 位随便放,然后去掉不合法的方案,即后 k k k 个都大于 d d d 的方案,由于序列前 i − 1 i-1 i−1 位是合法的,所以当后 k k k 个都大于 d d d 时,第 i − k i-k i−k 个一定为 d d d,所以方案数为 f i − k − 1 f_{i-k-1} fi−k−1。
当考虑一段相等的 c [ l , r ] c_{[l,r]} c[l,r] 时,假如相邻的 c c c 比它大,比如 c l − 1 > c l c_{l-1}>c_l cl−1>cl,那么 a [ l − 1 , l + k − 2 ] a_{[l-1,l+k-2]} a[l−1,l+k−2] 这一段已经被前面的 c c c 考虑了,而 c l + k − 1 c_{l+k-1} cl+k−1 一定为 c l c_l cl,也不需要考虑,所以需要 d p dp dp 的部分就少了 k k k 个,去掉他们再 d p dp dp 即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
#define mod 1000000007
int n,k,a[maxn],ans=1;
int f[maxn];
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int dp(int ln,int mi){
int p=1e9-mi,p_=ksm(p,k);f[0]=1;
for(int i=1;i<=ln;i++){
f[i]=1ll*f[i-1]*(p+1)%mod;
if(i>=k)f[i]=(f[i]-1ll*p_*(i>k?f[i-k-1]:1)%mod+mod)%mod;
}
return f[ln];
}
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n-k+1;i++)scanf("%d",&a[i]);
for(int i=1;i<=n-k+1;i++){
int j=i;while(a[j+1]==a[i])j++;
int len=j-i+k;
if(i>1&&a[i-1]>a[i])len-=k;
if(j<n-k+1&&a[j+1]>a[j])len-=k;
if(len>0)ans=1ll*ans*dp(len,a[i])%mod;
i=j;
}
printf("%d",ans);
}