luogu P1637 三元上升子序列

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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值