[区间dp]Valid Bitonic Permutations Codeforces1763D

You are given five integers nn, ii, jj, xx, and yy. Find the number of bitonic permutations BB, of the numbers 11 to nn, such that Bi=xBi=x, and Bj=yBj=y. Since the answer can be large, compute it modulo 109+7109+7.

A bitonic permutation is a permutation of numbers, such that the elements of the permutation first increase till a certain index kk, 2≤k≤n−12≤k≤n−1, and then decrease till the end. Refer to notes for further clarification.

Input

Each test contains multiple test cases. The first line contains a single integer tt (1≤t≤1001≤t≤100) — the number of test cases. The description of test cases follows.

The only line of each test case contains five integers, nn, ii, jj, xx, and yy (3≤n≤1003≤n≤100 and 1≤i,j,x,y≤n1≤i,j,x,y≤n). It is guaranteed that i<ji<j and x≠yx≠y.

Output

For each test case, output a single line containing the number of bitonic permutations satisfying the above conditions modulo 109+7109+7.

Example

input

7

3 1 3 2 3

3 2 3 3 2

4 3 4 3 1

5 2 5 2 4

5 3 4 5 4

9 3 7 8 6

20 6 15 8 17

output

0

1

1

1

3

0

4788

题意: 需要构造一个长度为n的排列,要求这个排列必须先增后减,且a[x] = vx, a[y] = vy,问这样的排列有多少个。

分析: 因为排列必须先增后减,所以构造的方法肯定是先选择一个最大值的位置,然后依次将n~1填入该位置两侧,数字放置的位置必须连续,否则构造不出来合法的排列。设dp[i][j]表示按这种方法填完区间[i, j]的合法方案数,如果不考虑两个数的限制,那递推方式就是dp[i][j] = dp[i+1][j]+dp[i][j-1],考虑最后一个要填的数字n-len+1放最左边还是最右边即可。加上这两个数的限制其实就是考虑一下n-len+1恰好等于vx或者vy的时候,假设n-len+1恰好等于vx,那么如果x等于i,此时vx只能放在i这个位置了,dp[i][j] = dp[i+1][j],若x等于j,则dp[i][j] = dp[i][j-1],如果x既不等于i也不等于j就说明不存在合法方案,直接dp[i][j] = 0。dp数组初始化的时候要看下vx或vy是否等于n,等于n的时候要特殊处理一下,然后因为数组必须先增后减,所以n这个数字肯定不能放在a[1]或a[n]处,要设置dp[1][1] = dp[n][n] = 0。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long
using namespace std;

int dp[105][105];
const int mod = 1e9+7;

signed main(){
	int T;
	cin >> T;
	while(T--){
		int n, x, y, vx, vy;
		scanf("%lld%lld%lld%lld%lld", &n, &x, &y, &vx, &vy);
		if(vx != n && vy != n)
			for(int i = 1; i <= n; i++){
				if(i != x && i != y) dp[i][i] = 1;
				else dp[i][i] = 0;
			}
		else if(vx == n){
			for(int i = 1; i <= n; i++){
				if(i == x) dp[i][i] = 1;
				else dp[i][i] = 0;
			}
		}
		else{
			for(int i = 1; i <= n; i++){
				if(i == y) dp[i][i] = 1;
				else dp[i][i] = 0;
			}
		}
		dp[1][1] = dp[n][n] = 0;
		for(int len = 2; len <= n; len++){
			for(int l = 1; l+len-1 <= n; l++){
				int r = l+len-1;
				//考虑把n-len+1放哪个位置
				if(n-len+1 == vx){
					if(l == x) dp[l][r] = dp[l+1][r];
					else if(r == x) dp[l][r] = dp[l][r-1];
					else dp[l][r] = 0;
				} 
				else if(n-len+1 == vy){
					if(l == y) dp[l][r] = dp[l+1][r];
					else if(r == y) dp[l][r] = dp[l][r-1];
					else dp[l][r] = 0;
				}
				else dp[l][r] = (dp[l+1][r]+dp[l][r-1])%mod;
			}
		}
//		for(int i = 1; i <= n; i++, putchar('\n'))
//			for(int j = 1; j <= n; j++)
//				cout << dp[i][j] << " ";
		printf("%lld\n", dp[1][n]);
	}

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值