bzoj 4621 Tc605 思想+dp
4621: Tc605
Time Limit: 15 Sec Memory Limit: 128 MBSubmit: 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
3 1 2
Sample Output
4
HINT
Source
这道题的思想王聿中大神十分牛逼。
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 }