The position of the leftmost maximum on the segment [l;r][l;r] of array x=[x1,x2,…,xn]x=[x1,x2,…,xn] is the smallest integer ii such that l≤i≤rl≤i≤r and xi=max(xl,xl+1,…,xr)xi=max(xl,xl+1,…,xr).
You are given an array a=[a1,a2,…,an]a=[a1,a2,…,an] of length nn. Find the number of integer arrays b=[b1,b2,…,bn]b=[b1,b2,…,bn] of length nn that satisfy the following conditions:
- 1≤bi≤m1≤bi≤m for all 1≤i≤n1≤i≤n;
- for all pairs of integers 1≤l≤r≤n1≤l≤r≤n, the position of the leftmost maximum on the segment [l;r][l;r] of the array bb is equal to the position of the leftmost maximum on the segment [l;r][l;r] of the array aa.
Since the answer might be very large, print its remainder modulo 109+7109+7.
Input
Each test contains multiple test cases. The first line contains a single integer tt (1≤t≤1031≤t≤103) — the number of test cases.
The first line of each test case contains two integers nn and mm (2≤n,m≤2⋅1052≤n,m≤2⋅105, n⋅m≤106n⋅m≤106).
The second line of each test case contains nn integers a1,a2,…,ana1,a2,…,an (1≤ai≤m1≤ai≤m) — the array aa.
It is guaranteed that the sum of n⋅mn⋅m over all test cases doesn't exceed 106106.
Output
For each test case print one integer — the number of arrays bb that satisfy the conditions from the statement, modulo 109+7109+7.
Example
input
4
3 3
1 3 2
4 2
2 2 2 2
6 9
6 9 6 9 6 9
9 100
10 40 20 20 100 60 80 60 60
output
8
5
11880
351025663
题意: 给出一个长度为n的数组a,要求用小于等于m的数字构造出一个数组b,并且每个子区间[l, r]内最大值第一次出现的位置要相同。
分析: 一开始对这题毫无思路,后来看题解知道了笛卡尔树这东西,笛卡尔树其实就是按照下标构造二叉搜索树,然后保证父节点权值大于子结点,其构造过程就是先找到数组中的最大值作为根,然后左区间内最大值作为左儿子,右区间内最大值作为右儿子。根据笛卡尔树的定义可知题目中的a数组和b数组应该具有相同结构的笛卡尔树,接下来就是统计这样的树有多少棵了,这个问题可以用树形dp去解决。
设dp[i][j]表示i结点值取j时子树中的方案数,设l[i]表示i结点左儿子,r[i]表示i结点右儿子,若两儿子均存在,那么dp[i][j] = sum(dp[l[i]][j-1]*dp[r[i]][j]), i∈[1, m],若只有左儿子存在,那么dp[i][j] = sum(dp[l[i]][j-1]), i∈[1, m],若只有右儿子存在,那么dp[i][j] = sum(dp[r[i]][j]), i∈[1, m],若是叶子结点,那么dp[i][j]均等于1。最后直接这么更新会tle,维护一下前缀和就好了。
具体代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+10, mod = 1e9+7;
int n, m, v[N], fa[N], ls[N], rs[N], s[N];
bool vis[N];
vector<vector<int>> dp;//dp[i][j]表示在i结点子树中,i的值取j时方案数
vector<vector<int>> sum;//dp[i][j]的前缀和数组
void Tree(){
scanf("%lld%lld", &n, &m);
int top = 0;
for(int i = 1; i <= n; i++)
ls[i] = rs[i] = 0;
for(int i = 1; i <= n; i ++){
scanf("%lld",&v[i]);
while(top && v[s[top]] < v[i])
ls[i] = s[top], top--;
fa[i] = s[top];
fa[ls[i]] = i;
if(fa[i]) rs[fa[i]] = i;
s[++top] = i;
}
}
void dfs(int now){
vis[now] = true;
//叶子结点
if(ls[now] == 0 && rs[now] == 0){
for(int i = 1; i <= m; i++){
dp[now][i] = 1;
sum[now][i] = (sum[now][i-1]+dp[now][i])%mod;
}
return;
}
for(int i = 1; i <= m; i++){
if(ls[now] && rs[now]){
if(!vis[ls[now]])
dfs(ls[now]);
if(!vis[rs[now]])
dfs(rs[now]);
dp[now][i] = (sum[ls[now]][i-1]*sum[rs[now]][i])%mod;
}
else if(ls[now]){
if(!vis[ls[now]])
dfs(ls[now]);
dp[now][i] = sum[ls[now]][i-1];
}
else{
if(!vis[rs[now]])
dfs(rs[now]);
dp[now][i] = sum[rs[now]][i];
}
sum[now][i] = (sum[now][i-1]+dp[now][i])%mod;
}
}
signed main(){
int T;
cin >> T;
while(T--){
Tree();
vector<vector<int>> temp(n+1, vector<int>(m+1));
dp = temp;
sum = temp;
for(int i = 1; i <= n; i++)
vis[i] = false;
int root = max_element(v+1, v+n+1)-v;
dfs(root);
printf("%lld\n", sum[root][m]);
}
return 0;
}