bzoj 4621 Tc605

$dp$ .

  • 一个数字只可以往左右拓展,把周围的数变成和它一样的数.所以最终状态一定是一些数字段拼起来的,而且数字之间相对的前后顺序不会改变.
  • 对最终状态进行 $dp$ .设 $f(i,j)$ 表示考虑了前 $i$ 个位置,操作了 $j$ 次的情况总数.一个数字段最多操作一次,处理出每个数字左右延伸的范围,枚举拓展的右端点进行转移即可.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int P=1e9+7;
inline int add(int a,int b)
{
return (a + b) % P;
}
inline int mul(int a,int b)
{
return 1LL * a * b % P;
}
const int MAXN=512;
int n,m,a[MAXN],f[MAXN][MAXN];
int main()
{
n=read(),m=read();
for(int i=1;i<=n;++i)
a[i]=read();
f[0][0]=1;
for(int i=1;i<=n;++i)
{
int l=i;
while(l>1 && a[l-1]<a[i])
--l;
int r=i;
while(r<n && a[r+1]<a[i])
++r;
f[i][m]=add(f[i][m],f[i-1][m]);
for(int j=m;j>=1;--j)
{
int tmp=f[l-1][j-1];
for(int k=l;k<=r;++k)
{
f[k][j]=add(f[k][j],tmp);
tmp=add(tmp,f[k][j-1]);
}
f[i][j-1]=add(f[i][j-1],f[i-1][j-1]);
f[i][j]=add(f[i][j],-f[i-1][j-1]);
}
}
int ans=0;
for(int i=0;i<=m;++i)
ans=add(ans,f[n][i]);
cout<<add(ans,P)<<endl;
return 0;
}