题目地址:https://iai.sh.cn/problem/100
题目描述
给定正整数 n n n 及 1 1 1 到 n n n 的一个排列 a 1 , a 2 , ⋯ , a n a_1,a_2,⋯ ,a_n a1,a2,⋯ ,an,请统计其中有多少由三个数字组成的三元组,满足 132 型。所谓 132 型,是指三个下标 ( i , j , k ) (i,j,k) (i,j,k),满足 i < j < k i<j<k i<j<k且 a i < a k < a j ai<ak<aj ai<ak<aj 。即,中间的数字最大,尾部的数字第二大,头部的数字最小。
输入格式
第一行:单个正整数
n
n
n
第二行:
n
n
n 个正整数
a
1
,
a
2
,
⋯
,
a
n
a_1,a_2,⋯ ,a_n
a1,a2,⋯ ,an。
输出格式
单个整数:表示符合条件的三元组数量。注意这个数字可能超过 2 32 2^{32} 232
数据范围
对于
30
30
30% 的数据,
1
≤
n
≤
300
1≤n≤300
1≤n≤300
对于
60
60
60% 的数据,
1
≤
n
≤
5000
1≤n≤5000
1≤n≤5000
对于
100
100
100的数据,
1
≤
n
≤
20000
1≤n≤20000
1≤n≤20000
样例数据
输入:
5
1 3 2 5 4
输出:
4
说明:
(1,3,2),(1,5,4),(3,5,4),(2,5,4)共四组
分析:暴力
O
(
n
3
)
O(n^3)
O(n3),肯定会
T
L
E
TLE
TLE
开始乱搞
考虑每个值对答案的贡献
首先一维肯定是要枚举的,选择
(
i
,
j
,
k
)
(i,j,k)
(i,j,k)中的
i
i
i进行枚举可以减少限制因为它位置最前且值最小。
考虑倒序枚举,正序无法维护
j
,
k
j,k
j,k
接下来就是
j
,
k
j,k
j,k,我们发现只要在右边保证
j
>
k
j>k
j>k 且
a
[
j
]
<
a
[
k
]
a[j]<a[k]
a[j]<a[k]可以用值域线段树维护,开一棵权值线段树,枚举到每一个数
i
i
i,将线段树上已有比它小的点的值都加一,然后是统计,所以任意结点的值代表在它左边存在多少个数,满足
23
型
23型
23型,
j
<
k
j<k
j<k 且
a
[
j
]
>
a
[
k
]
a[j]>a[k]
a[j]>a[k],每扫到一个点,先统计线段树上值比它大的点就可以了
动态开点线段树,区间修改,区间查询和
C o d e Code Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=2e4;
struct SEG{
int lc,rc,s,l,r,tag,num;
}tree[MAXN<<2|10];
struct w{
int val,op;
}b[MAXN+10];
int a[MAXN+10],c[MAXN+10],d[MAXN+10];
int tot=0,root=0;
inline int read();
inline bool cmp(const w&,const w&);
inline void pushup(int);
inline void pushdown(int);
void Insert(int,int,int,int&);
void update(int,int,int,int);
int query(int,int,int);
signed main(){
//freopen ("std.in","r",stdin);
//freopen ("std.out","w",stdout);
memset(tree,0,sizeof(tree));
int n=read();
for (register int i=1;i<=n;++i){
b[i].val=a[i]=read();
b[i].op=i;
}
sort(b+1,b+n+1,cmp);
int ans=0;
for (register int i=1;i<=n;++i){
c[b[i].op]=i;
d[i]=b[i].val;
}
for (register int i=n;i>=1;--i){
int x=c[i];
//int y=c[i]+1;
int y=lower_bound(d+1,d+n+1,b[x].val+1)-d;
ans+=query(y,n,root);
//y=c[i]-1;
y=lower_bound(d+1,d+n+1,b[x].val)-d-1;
update(1,y,root,1);
Insert(x,1,n,root);
}
if (tree[0].tag==0 && tree[0].s==0) printf("%lld\n",ans);
return 0;
}
inline int read(){
int x=0;
char c=getchar();
while (!isdigit(c))c=getchar();
while (isdigit(c))x=(x<<1)+(x<<3)+(c&15),c=getchar();
return x;
}
inline bool cmp(const w &x,const w &y){
return x.val<y.val;
}
inline void pushup(int s){
tree[s].s=tree[tree[s].lc].s+tree[tree[s].rc].s;
tree[s].num=tree[tree[s].lc].num+tree[tree[s].rc].num;
}
inline void pushdown(int s){
int val=tree[s].tag;
if (tree[s].lc){
int lc=tree[s].lc;
tree[lc].s+=val*tree[lc].num;
tree[lc].tag+=val;
}
if (tree[s].rc){
int rc=tree[s].rc;
tree[rc].s+=val*tree[rc].num;
tree[rc].tag+=val;
}
tree[s].tag=0;
return;
}
void Insert(int x,int l,int r,int &rt){
if (!rt){
rt=++tot;
tree[rt].l=l,tree[rt].r=r;
tree[rt].tag=tree[rt].s=0;
tree[rt].num=1;
}
if (l==r) return;
int mid=l+r>>1;
pushdown(rt);
if (x<=mid) Insert(x,l,mid,tree[rt].lc);
else Insert(x,mid+1,r,tree[rt].rc);
pushup(rt);
}
void update(int l,int r,int s,int val){
if (tree[s].l>r || tree[s].r<l) return;
if (l<=tree[s].l && tree[s].r<=r){
tree[s].tag+=val;
tree[s].s+=val*tree[s].num;
return;
}
pushdown(s);
update(l,r,tree[s].lc,val);
update(l,r,tree[s].rc,val);
pushup(s);
}
int query(int l,int r,int s){
if (tree[s].r<l || tree[s].l>r) return 0;
if (l<=tree[s].l && tree[s].r<=r) return tree[s].s;
pushdown(s);
return query(l,r,tree[s].lc) + query(l,r,tree[s].rc);
}

该博客介绍了如何利用线段树解决寻找满足132型排列条件的三元组问题。博主分析了暴力解法会超时的问题,提出通过倒序枚举并使用值域线段树来优化算法,动态维护满足条件的三元组数量。内容包括题目描述、输入输出格式、数据范围以及样例数据,并提供了代码实现。
839

被折叠的 条评论
为什么被折叠?



