CF1677A Tokitsukaze and Strange Inequality 题解
题目
链接
https://www.luogu.com.cn/problem/CF1677A
字面描述
题目描述
给一个长度为 n n n 的排列,求有多少个四元组 ( a , b , c , d ) (a,b,c,d) (a,b,c,d) 满足 a < b < c < d a<b<c<d a<b<c<d 和 p a < p c p_a<p_c pa<pc、 p b > p d p_b>p_d pb>pd。
输入格式
第一行包含一个正整数 t t t ( 1 ≤ t ≤ 1000 1 \leq t \leq 1000 1≤t≤1000 )——表示小测试点总数,每个小测试点包含 2 2 2行输入
第一行 一个正整数 n ( 4 ≤ n ≤ 5000 ) n(4 \leq n \leq 5000) n(4≤n≤5000)表示 p : p: p:{} 数组的长度
第二行包含 n n n个正整数 p 1 , p 2 , … , p n p_1, p_2, \ldots, p_n p1,p2,…,pn ( 1 ≤ p i ≤ n 1 \leq p_i \leq n 1≤pi≤n) — 表示数组 p p p内的元素 .
题目保证 ∑ n ≤ 5000 \sum n \leq 5000 ∑n≤5000
输出格式
对于每个小测试点输出一个非负整数表示合法四元组的数量
样例 #1
样例输入 #1
3
6
5 3 6 1 4 2
4
1 2 3 4
10
5 1 6 2 8 3 4 10 9 7
样例输出 #1
3
0
28
提示
对于第一组样例合法四元组 ( p a , p b , p c , p d ) (p_a,p_b,p_c,p_d) (pa,pb,pc,pd)为
- ( 5 , 3 , 6 , 1 ) (5,3,6,1) (5,3,6,1)
- ( 5 , 3 , 6 , 2 ) (5,3,6,2) (5,3,6,2)
- ( 3 , 6 , 4 , 2 ) (3,6,4,2) (3,6,4,2)
思路点拨
暴力思想
先不提优化,如果用最暴力的想法去解决这道题无非分别枚举
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d的值,时间复杂度将达到
O
(
n
4
)
≈
6.25
⋅
1
0
14
\Omicron(n^4) \approx 6.25 \cdot 10^{14}
O(n4)≈6.25⋅1014
肯定 TLE
经验借鉴
这类题,借助李学长的优化思路
可将这类题归纳为借助题目性质+预处理优化
优化
∵
四元组满足
p
a
<
p
c
且
p
b
>
p
d
\because 四元组满足p_a<p_c且p_b>p_d
∵四元组满足pa<pc且pb>pd
∴
\therefore
∴枚举
b
,
c
b,c
b,c的范围,分别对
a
,
d
a,d
a,d提前进行前缀和和后缀和预处理
定义如下
- l e s s l i j lessl_{i_j} lesslij表示 ∑ p k < i ( 1 ≤ k ≤ j ) \sum p_k<i(1 \leq k \leq j) ∑pk<i(1≤k≤j)
- 同理 l e s s r i j lessr_{i_j} lessrij表示 ∑ p k < i ( j ≤ k ≤ n ) \sum p_k<i(j \leq k \leq n) ∑pk<i(j≤k≤n)
最后只需要枚举 b , c b,c b,c下标直接依靠乘法原理完成计算
复杂度分析
感谢题目
1
≤
p
i
≤
n
1 \leq p_i \leq n
1≤pi≤n方便处理
l
e
s
s
less
less{}
空间复杂度:
O
(
n
2
)
\Omicron(n^2)
O(n2)
时间复杂度:
O
(
n
2
)
\Omicron(n^2)
O(n2)
- 预处理时间复杂度: O ( n 2 ) \Omicron(n^2) O(n2)
- 计算时间复杂度: O ( n 2 ) \Omicron(n^2) O(n2)
记得最后只要
a
n
s
ans
ans开
l
o
n
g
l
o
n
g
long long
longlong
不开
W
A
WA
WA
全开
M
L
E
MLE
MLE
只开
A
C
AC
AC
代码实现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e3+10;
int t,n;
ll ans;
int a[maxn];
int lessl[maxn][maxn],lessr[maxn][maxn];
//预处理
inline void init(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
lessl[i][j]=lessl[i][j-1];
if(a[j]<i)++lessl[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=n;j>=1;j--){
if(j!=n)lessr[i][j]=lessr[i][j+1];
else lessr[i][j]=0;
if(a[j]<i)++lessr[i][j];
}
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
init();
ans=0;
//计算
for(int i=2;i<=n-2;i++){
for(int j=i+1;j<=n-1;j++)ans=(ll)ans+lessl[a[j]][i-1]*lessr[a[i]][j+1];
}
printf("%lld\n",ans);
}
return 0;
}