【题目大意】
给出一个数组A,求出满足i<j<k且Ai<Aj>Ak的数对(i,j,k)的个数
【解题思路】
第一反应是三元逆序对或者类似的东西,但发现做不出来,于是换思路。
想了一会发现可以直接维护两颗线段树,但是压入的是数值不是下标,一颗处理一下区间和
最后倒回来枚举j求出一个left值和right值(即j对答案的贡献),乘起来累加进答案即可
【代码】
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cctype>
#include<iomanip>
#define LL long long
//#define LOCAL
using namespace std;
const int N=50011;
int n,m;
int a[N];
LL ans=0;
struct Seg_Tree{
struct treenode{
int l,r;
LL sum;
int lazy;
void Update(int v,int len){
sum+=1LL*len*v;
lazy+=v;
}
}tree[N<<2];
void UnleashLabel(int o,int len){
int llen=len>>1;
int rlen=len-llen;
tree[o<<1].lazy+=tree[o].lazy;
tree[o<<1|1].lazy+=tree[o].lazy;
tree[o<<1].sum+=1LL*llen*tree[o].lazy;
tree[o<<1|1].sum+=1LL*rlen*tree[o].lazy;
tree[o].lazy=0;
}
void Maintain(int o){
tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
}
void Build(int o,int l,int r){
tree[o].l=l;
tree[o].r=r;
if (l==r) return;
else{
int mid=(l+r)>>1;
Build(o<<1,l,mid);
Build(o<<1|1,mid+1,r);
}
}
void Modify(int o,int ql,int qr,int v){
int l=tree[o].l;
int r=tree[o].r;
if (ql<=l&&qr>=r){
tree[o].Update(v,r-l+1);
return;
}
else{
UnleashLabel(o,r-l+1);
int mid=(l+r)>>1;
if (qr<=mid) Modify(o<<1,ql,qr,v);
else if (ql>mid) Modify(o<<1|1,ql,qr,v);
else{
Modify(o<<1,ql,mid,v);
Modify(o<<1|1,mid+1,qr,v);
}
Maintain(o);
}
}
LL Query(int o,int ql,int qr){
int l=tree[o].l;
int r=tree[o].r;
if (ql<=l&&qr>=r) return tree[o].sum;
else{
UnleashLabel(o,r-l+1);
LL ret=0;
int mid=(l+r)>>1;
if (qr<=mid) ret+=Query(o<<1,ql,qr);
else if (ql>mid) ret+=Query(o<<1|1,ql,qr);
else ret+=Query(o<<1,ql,mid)+Query(o<<1|1,mid+1,qr);
return ret;
}
}
};
Seg_Tree A,B;
int main(){
#ifdef LOCAL
freopen("UESTC841.in","r",stdin);
#endif
while (scanf("%d",&n)!=EOF){
ans=0;
A.Build(1,0,32768);
B.Build(1,0,32768);
for (int i=1;i<=n;++i){
scanf("%d",&a[i]);
A.Modify(1,a[i],a[i],1);
}
for (int i=n;i>=1;--i){
int k=a[i];
LL L=A.Query(1,0,k-1);
LL R=B.Query(1,0,k-1);
ans+=1LL*L*R;
A.Modify(1,a[i],a[i],-1);
B.Modify(1,a[i],a[i],1);
}
printf("%I64d\n",ans);
}
return 0;
}
【总结】
类似逆序对的题的求法——权值线段树预处理