bzoj 4621 Tc605 思想+dp

4621: Tc605

Time Limit: 15 Sec  Memory Limit: 128 MB
Submit: 328  Solved: 183
[Submit][Status][Discuss]

Description

最初你有一个长度为 N 的数字序列 A。为了方便起见,序列 A 是一个排列。
你可以操作最多 K 次。每一次操作你可以先选定一个 A 的一个子串,然后将这个子串的数字全部变成原来这个子串的最大值。问最终有几种可能的数字序列。答案对 1e9+7 取模。

Input

第一行两个数 N 和 K。第二行 N 个数,描述一个排列 A。 
N,K<=500,
有6组数据N>100,有梯度

Output

输出一个数,表示答案在模域下的值。 

Sample Input

3 2
3 1 2

Sample Output

4

HINT

Source

TC 605 Hard

 

这道题的思想王聿中大神十分牛逼。

https://www.cnblogs.com/wangyurzee7/p/5554380.html

发现对于一个数可以操作的范围是确定的,然后

发现最终的序列中,除了没有操作的序列,如果操作了一定是1-k之间的段数。

f[i][j]表示分成了i段数,是1-j这些数产生的方案数,为什么i为段数,

因为在更新的时候,比如x这个点,可以操作的范围是l--r,那么从f[i-1][l-r]中更新。

用到了前缀和优化降低一维,变成了n^3

 1 #pragma GCC optimize(2)
 2 #pragma G++ optimize(2)
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<cstdio>
 7 #include<cstring>
 8 
 9 #define N 507
10 #define mod 1000000007
11 #define ll long long
12 using namespace std;
13 inline int read()
14 {
15     int x=0,f=1;char ch=getchar();
16     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
17     while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
18     return x*f;
19 }
20 
21 int n,K;
22 int a[N];
23 int f[N][N],delta[N];
24 
25 int main()
26 {
27     f[0][0]=1;
28     n=read(),K=read();
29     for (int i=1;i<=n;i++)
30         a[i]=read();
31     for (int i=1;i<=n;i++)
32     {
33         int l,r;
34         for (l=i;l>1&&a[l-1]<a[i];l--);
35         for (r=i;r<n&&a[r+1]<a[i];r++);
36         (f[K][i]+=f[K][i-1])%=mod;
37         for (int k=K-1;k>=0;k--)
38         {
39             delta[l-1]=0;
40             for (int j=l;j<=r;j++)delta[j]=(delta[j-1]+f[k][j-1])%mod;
41             for (int j=l;j<=r;j++)(f[k+1][j]+=delta[j])%=mod;
42             (f[k][i]+=f[k][i-1])%=mod;//什么都不操作
43             (f[k+1][i]+=mod-f[k][i-1])%=mod;//自己这个已经加过了
44         }
45     }
46     int ans=0;
47     for (int i=0;i<=K;i++)
48         (ans+=f[i][n])%=mod;
49     printf("%d\n",ans);
50 }

 

posted @ 2018-03-01 10:57  Kaiser-  阅读(236)  评论(0编辑  收藏  举报