luogu P1637 三元上升子序列
十分有意思的一道线段树
首先:
哇!30000!暴力!
然后…
63
我们来考虑优化!
用线段树
考虑每次加入一个数前先计算比它小的数的个数
可以用f[i]表示i这个数字的出现次数
答案=f[0]+…+f[i-1]
比它大的也这样算
答案=l[1]*r[1]+…+l[n]+r[n]
l[i]表示在i左边且比a[i]小的个数
r[i]表示在i右边且比a[i]大的个数
然后!
记得离散化。
AC!
std:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=30005;
int n;
int a[N];
ll l[N]={0},r[N]={0};
ll ans=0;
int main()
{
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<i;j++)
{
if(a[j]<a[i])
l[i]++;
}
}
for(int i=1;i<=n;i++)
{
for(int j=n;j>i;j--)
{
if(a[j]>a[i])
r[i]++;
}
}
for(int i=1;i<=n;i++)
ans+=l[i]*r[i];
printf("%lld\n",ans);
return 0;
}
啊呀,你怎么知道我给的是暴力?
std:
#include<bits/stdc++.h>
using namespace std;
const int N=30005;
typedef long long ll;
struct node{
ll x;
int rank;
};
int n;
node a[N];
ll t[N*20]={0};
ll l[N]={0},r[N]={0};
ll ans=0;
ll num[N];
ll C2(ll x){
ll a=x,b=x-1;
if(x<=1) return 0;
if(a%2==0) a/=2;
else b/=2;
return a*b;
}
ll C3(ll x){
ll a=x,b=x-1,c=x-2;
if(x<=2) return 0;
if(a%2==0) a/=2;
else b/=2;
if(a%3==0) a/=3;
else if(b%3==0) b/=3;
else c/=3;
return a*b*c;
}
bool cmp(node x,node y){
if(x.x==y.x) return x.rank<y.rank;
else return x.x<y.x;
}
void upt(int l,int r,int num,int pos){
if(l>pos||r<pos) return;
if(l==r&&r==pos){
t[num]++;
return;
}
int mid=(l+r)>>1;
upt(l,mid,num<<1,pos);
upt(mid+1,r,num<<1|1,pos);
t[num]++;
}
int serch(int l,int r,int num,int L,int R){
if(l>R||r<L) return 0;
if(l>=L&&r<=R) return t[num];
int mid=(l+r)>>1;
return serch(l,mid,num<<1,L,R)+serch(mid+1,r,num<<1|1,L,R);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i].x),a[i].rank=i;
sort(a+1,a+n+1,cmp);
int cnt=0;
a[0].x=-2910;
for(int i=1;i<=n;i++){
if(a[i].x>a[i-1].x) cnt++;
num[a[i].rank]=cnt;
}
for(int i=1;i<=n;i++){
upt(1,n,1,num[i]);
l[i]=serch(1,n,1,1,num[i]-1);
}
memset(t,0,sizeof(t));
for(int i=n;i>=1;i--){
upt(1,n,1,num[i]);
r[i]=serch(1,n,1,num[i]+1,n);
}
for(int i=1;i<=n;i++){
ans=ans+l[i]*r[i];
}
printf("%lld\n",ans);
return 0;
}