[前缀和]Tokitsukaze and Strange Inequality Codeforces1678C

11 篇文章 0 订阅
3 篇文章 0 订阅

Tokitsukaze has a permutation pp of length nn. Recall that a permutation pp of length nn is a sequence p1,p2,…,pnp1,p2,…,pn consisting of nn distinct integers, each of which from 11 to nn (1≤pi≤n1≤pi≤n).

She wants to know how many different indices tuples [a,b,c,d][a,b,c,d] (1≤a<b<c<d≤n1≤a<b<c<d≤n) in this permutation satisfy the following two inequalities:

pa<pcpa<pc and pb>pdpb>pd.

Note that two tuples [a1,b1,c1,d1][a1,b1,c1,d1] and [a2,b2,c2,d2][a2,b2,c2,d2] are considered to be different if a1≠a2a1≠a2 or b1≠b2b1≠b2 or c1≠c2c1≠c2 or d1≠d2d1≠d2.

Input

The first line contains one integer tt (1≤t≤10001≤t≤1000) — the number of test cases. Each test case consists of two lines.

The first line contains a single integer nn (4≤n≤50004≤n≤5000) — the length of permutation pp.

The second line contains nn integers p1,p2,…,pnp1,p2,…,pn (1≤pi≤n1≤pi≤n) — the permutation pp.

It is guaranteed that the sum of nn over all test cases does not exceed 50005000.

Output

For each test case, print a single integer — the number of different [a,b,c,d][a,b,c,d] tuples.

Example

input

3

6

5 3 6 1 4 2

4

1 2 3 4

10

5 1 6 2 8 3 4 10 9 7

output

3

0

28

题意: 有一个数组p,求有多少个四元组(a, b, c, d)满足p[a] < p[c] 且 p[b] > p[d],并且a < b < c < d。

分析: 数组长度比较小,最大只有5000,所以可以想到枚举a和c,然后只需要求出[c+1,n]之间每个d对应的在[a+1, c-1]之间的b的个数。可以维护一个前缀和数组num,num[i][j]表示前i个数中大于j的数有多少个,这样对于确定的区间[a+1, c-1]中满足p[b] > p[d]的个数就是num[c-1][p[d]]-num[a][p[d]],不过d的位置也是未知的,也需要枚举,时间复杂度就是O(n^3)了,接下来考虑优化掉最内层循环。定义sum[i][j]表示对于[j+1, n]中每个数d在[i+1, j-1]内大于d的b个数之和,这样在枚举完a和c后答案就可以加上sum[a][c],sum数组的维护可以通过一个简单的区间dp来求得,得到sum数组后这道题目就能够O(n^2)求解了。

还有另外一种简单的方法,维护前i个数中小于j的数字个数pre[i][j]以及后i个数中小于j的数字个数back[i][j],这样就可以枚举b和c,然后答案叠加pre[b-1][p[c]]*back[c+1][p[b]]。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;

int A[5005];
int num[5005][5005];//num[i][j]表示前i个数中有多少个大于a[j]的数 
int sum[5005][5005];//a~c的后缀和 
int sum2[5005];

signed main()
{
	int	T;
	cin >> T;
	while(T--){
		int n;
		scanf("%d", &n);
		for(int i = 1; i <= n; i++)
			scanf("%d", &A[i]);
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++){
				num[j][A[i]] = num[j-1][A[i]]; 
				sum2[j] = 0;
				sum[i][j] = 0;
				if(A[j] > A[i]) num[j][A[i]]++;
			}
		for(int i = 2; i <= n-2; i++)
			for(int j = i+2; j <= n; j++)
				if(A[i] > A[j]) sum2[i]++;
		for(int len = n-1; len >= 3; len--){
			for(int l = 1; l+len-1 <= n-1; l++){
				int r = l+len-1;
				sum[l][r] = sum[l][r+1]-sum2[r]+(num[r-1][A[r+1]]-num[l][A[r+1]]);
			}
		}
		long long ans = 0; 
		for(int a = 1; a+3 <= n; a++){
			for(int c = a+2; c+1 <= n; c++){
				if(A[a] >= A[c]) continue;
				ans += sum[a][c];
			}
		}
		printf("%lld\n", ans);
		
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值