题目
题目描述
琪露诺又开始学数学了——关于
x
x
x 的方程满足
1
+
1
=
x
1+1=x
1+1=x,求
x
x
x 的值。
众所周知,琪露诺无法解答这个问题,并对提出这个问题的你感到无比愤怒,于是提出了一个简单的问题,希望得到你的解答。
给出一个 1 1 1 到 n n n 的排列,每次的操作定义为:选择一个区间 [ l , r ] [l,r] [l,r] ,并将这个区间中的所有数变成这个区间中的最大的数,请问所有的能够通过这样的操作到达的排列有多少种呢?
但是聪明的你觉得十分简单于是反问琪露诺,如果我限制总的操作次数那么答案又是多少呢?
琪露诺无法回答这个问题,于是你需要给她答案。自作自受大概就是这个意思
输入格式
第一行包含一个整数
T
T
T 表示数据的组数。
对于每组数据的第一行两个整数 n , K n,K n,K 表示排列的大小和操作的次数。
接下来一行 n n n 个数构成一个排列。
输出格式
输出包含
T
T
T 行,每行输出一个答案,模
1
0
9
+
7
10^9+7
109+7 输出。
数据范围和约定
对于所有的数据满足
T
≤
10
T\le 10
T≤10;
对于前
10
%
10\%
10% 的数据,
n
≤
8
n\le 8
n≤8;
对于另外
30
%
30\%
30% 的数据,
n
≤
50
n\le 50
n≤50;
对于另外
60
%
60\%
60% 的数据,
n
≤
200
n\le 200
n≤200。
思路
猜猜最后构成的排列长什么样?也就是说,最后的序列有什么性质?
举个例子。最初的排列是 ⟨ 2 , 3 , 4 , 1 , 5 ⟩ \langle 2,3,4,1,5\rangle ⟨2,3,4,1,5⟩ 。最后生成的排列中, 3 3 3 不可能出现在最后(甚至不可能出现在后 3 3 3 位)。原因很简单,有一座高山 4 4 4 阻挡了 3 3 3 !
形式化地,若原排列为 ⟨ a ⟩ \langle a\rangle ⟨a⟩ ,最终的排列为 ⟨ b ⟩ \langle b\rangle ⟨b⟩ ,若 a i = b j a_i=b_j ai=bj ,则 a i = max t = i j a t a_i=\max_{t=i}^{j}a_t ai=maxt=ijat 。(其实应该写成 ∣ t − i ∣ + ∣ t − j ∣ = ∣ i − j ∣ |t-i|+|t-j|=|i-j| ∣t−i∣+∣t−j∣=∣i−j∣ ,因为 i , j i,j i,j 大小关系未知)。
而与之相对应的,其他地方都有可能。甚至有可能只出现在第 1 1 1 位。原因很简单,先覆盖第 1 1 1 位,然后第二位再被 4 4 4 覆盖。说白了就是 法无禁止皆可为。
最后有一个性质,数字间先后顺序不改变。综合起来就是等价条件了。
所以我们只需要预处理出每个数可能出现的区间。然后 d p dp dp !
用 f ( x , y , z ) f(x,y,z) f(x,y,z) 表示前 y y y 个数字共同填了长度为 x x x 的序列(或者说,最终的排列中,前 x x x 位只有原来的前 y y y 个值),搞了 z z z 次。
对于这个 z z z ,为了让计算方便,我们计算在每一个原序列的数字上进行了几次操作。即 1 1 1 到 y y y “身上”的费用。
- 第 y y y 个数被吃了!那么 f ( x , y , z ) = f ( x , y − 1 , z ) f(x,y,z)=f(x,y-1,z) f(x,y,z)=f(x,y−1,z) 。(等着别人覆盖它,所以没有费用)
- 第 y y y 个数恰好覆盖 i i i !如果 y y y 原本就在 i i i ,那就没有费用;否则就有费用。
- 第 y y y 个数覆盖了两位或更多!那么 f ( x , y , z ) = ∑ v = L y − 1 x − 2 f ( v , y − 1 , z − 1 ) f(x,y,z)=\sum_{v=L_y-1}^{x-2}f(v,y-1,z-1) f(x,y,z)=∑v=Ly−1x−2f(v,y−1,z−1) 。
发现第 3 3 3 种转移很慢。但是这是个前缀和,处理一下就好了。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <ctime>
using namespace std;
inline int readint(){
int a = 0, f = 1; char c = getchar();
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -1;
for(; '0'<=c and c<='9'; c=getchar())
a = a*10+c-'0';
return a*f;
}
const int MAXN=200,MOD=1e9+7;
int dp[MAXN+1][MAXN+1][MAXN+1], s[MAXN+1][MAXN+1][MAXN+1];
int a[MAXN+2], L[MAXN+1], R[MAXN+1];
int main(){
int n, times, ans;
for(int T=readint(); T; --T){
n = readint(), times = readint();
for(int i=1; i<=n; ++i)
a[i] = readint();
a[0] = a[n+1] = 2147483647; // 边缘
for(int i=1; i<=n; ++i){ // (L[i],R[i])
for(int j=i+1; j<=n+1; ++j)
if(a[i] < a[j]){
R[i] = j;
break;
}
for(int j=i-1; ~j; --j)
if(a[j] > a[i]){
L[i] = j;
break;
}
}
ans = 0;
for(int j=0; j<=n; ++j) // init
s[0][j][0] = dp[0][j][0] = 1;
for(int i=1; i<=n; ++i)// 填了i个位置
for(int j=0; j<=n; ++j)// 第j个数
for(int k=0; k<=times; ++k){
if(j) // j被吞掉了
dp[i][j][k] = dp[i][j-1][k];
if(i >= R[j]) // j也很绝望啊
goto XME;
if(i == j) // 没准儿没动?
dp[i][j][k] += dp[i-1][j-1][k];
else if(L[j] < i and k) // j只填了i
dp[i][j][k] += dp[i-1][j-1][k-1];
dp[i][j][k] -= dp[i][j][k]/MOD*MOD; // 取模
if(k and L[j] <= i-2) // 填了不止一位
dp[i][j][k] += s[i-2][j-1][k-1]-(L[j]?s[L[j]-1][j-1][k-1]:0);
dp[i][j][k] -= dp[i][j][k]/MOD*MOD;
if(dp[i][j][k] < 0)
dp[i][j][k]+=MOD;
XME: // s[i][j][k] = \sum_{v=0}^{i} dp[v][j][k]
s[i][j][k] = s[i-1][j][k]+dp[i][j][k];
s[i][j][k] -= s[i][j][k]/MOD*MOD;
}
for(int k=0; k<=times; ++k)
ans += dp[n][n][k], ans %= MOD;
printf("%d\n",ans);
}
return 0;
}