[笛卡尔树][树形dp]Yet Another Array Counting Problem Codeforces1748E

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值