【BZOJ4621】Tc605
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
題解:好題。
先預處理出每個數左邊和右邊第一個比它大的數的位置ls,rs,然後将這個數看成一個區間,問題就變成了選出一些區間覆寫整個序列的方案數。用f[i][j]表示覆寫到位置i,已選擇了j個區間的方案數。那麼對于所有ls<=i<=rs,用$\sum\limits_{k=ls-1}^{i-1}f[k][j-1]$更新即可,顯然可以用字首和優化。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int P=1000000007;
int n,m,ans,sum;
int f[510][510],v[510];
int main()
{
scanf("%d%d",&n,&m);
int i,j,k,ls,rs;
for(i=1;i<=n;i++) scanf("%d",&v[i]);
f[0][0]=1;
for(i=1;i<=n;i++)
{
for(ls=i;ls>1&&v[ls-1]<v[i];ls--);
for(rs=i;rs<n&&v[rs+1]<v[i];rs++);
for(j=m;j>=0;j--)
{
f[i][j]=(f[i][j]+f[i-1][j])%P;
if(j)
{
sum=0;
for(k=ls;k<=rs;k++) sum=(sum+f[k-1][j-1])%P,f[k][j]=(f[k][j]+sum)%P;
f[i][j]=(f[i][j]-f[i-1][j-1]+P)%P;
}
}
}
for(i=0;i<=m;i++) ans=(ans+f[n][i])%P;
printf("%d",ans);
return 0;
}//3 2 1 2 3
轉載于:https://www.cnblogs.com/CQzhangyu/p/7670208.html