洛谷 P1637 三元上升子序列
题目描述
Erwin 最近对一种叫 thair 的东西巨感兴趣。。。
在含有 n n n 个整数的序列 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,…,an 中,三个数被称作thair当且仅当 i < j < k i<j<k i<j<k 且 a i < a j < a k a_i<a_j<a_k ai<aj<ak。
求一个序列中 thair 的个数。
输入格式
开始一行一个正整数 n n n,
以后一行 n n n 个整数 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,…,an。
输出格式
一行一个整数表示 thair 的个数。
样例
输入 #1
4
2 1 3 4
输出 #1
2
输入 #2
5
1 2 2 3 4
输出 #2
7
典型的权值线段树,将数据先离散,然后对每个数 a [ i ] a[i] a[i] 在线段树中找比它小的 l [ i ] l[i] l[i] 和 r [ i ] r[i] r[i] 即可,最后累加 l [ i ] ∗ r [ i ] l[i]*r[i] l[i]∗r[i] 就是答案,AC代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e4+5;
typedef long long ll;
int n,m,k;
ll a[N],l[N],r[N],tree[N<<2];
vector<ll>v;
int getpos(ll x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void pushup(int i)
{
tree[i]=tree[i<<1]+tree[i<<1|1];
}
void build(int i,int l,int r)
{
if(l==r)
{
tree[i]=0;
return ;
}
int mid=l+r>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
pushup(i);
}
void update(int i,int l,int r,int x,int k)
{
if(l==r)
{
tree[i]+=k;
return ;
}
int mid=l+r>>1;
if(x<=mid) update(i<<1,l,mid,x,k);
else update(i<<1|1,mid+1,r,x,k);
pushup(i);
}
ll query(int i,int l,int r,int m,int n)
{
if(m<=l&&r<=n) return tree[i];
int mid=l+r>>1;
ll ans=0;
if(m<=mid) ans+=query(i<<1,l,mid,m,n);
if(n>mid) ans+=query(i<<1|1,mid+1,r,m,n);
return ans;
}
int main(){
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
n=v.size();
for(int i=1;i<=m;i++){
int pos=getpos(a[i]);
if(pos!=1) l[i]=query(1,1,n,1,pos-1);
update(1,1,n,getpos(a[i]),1);
}
memset(tree,0,sizeof((tree)));
for(int i=m;i>=1;i--){
int pos=getpos(a[i]);
if(pos!=n) r[i]=query(1,1,n,getpos(a[i])+1,n);
update(1,1,n,getpos(a[i]),1);
}
ll ans=0;
for(int i=1;i<=m;i++) ans+=l[i]*r[i];
printf("%lld",ans);
return 0;
}