楼兰图腾

题目链接

题意:给你n个点分别为(i,y[i])

如果三个点(i,yi),(j,yj),(k,yk)(i,yi),(j,yj),(k,yk)满足1i<j<knyi>yj,yj<yk1≤i<j<k≤n且yi>yj,yj<yk,则称这三个点构成V图腾;

如果三个点(i,yi),(j,yj),(k,yk)(i,yi),(j,yj),(k,yk)满足1i<j<knyi<yj,yj>yk1≤i<j<k≤n且yi<yj,yj>yk,则称这三个点构成∧图腾;

求V和^图腾的个数。

思路:通过树状数组求逆序对的方法求得答案。树状数组求逆序对就是维护一个a[i]范围的区间,进行区间内+1操作,这样后面的后缀多一个,再通过逆序对的性质进行求解。

比如V先1~n遍历找每个位置多少个数比当前大,再n~1遍历找每个位置多少个数比当前小。^亦如此。

和算法竞赛指南上的求逆序对不太一样,因为它是找比其小的,效果还是一样的。

#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<cstdio>
#include<cmath>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
const int N=2e5+10;
ll ans,a[N],c[N],l[N],r[N],n,max_val;
ll ask(ll x)
{
    ll ans=0;
    for(;x;x-=lowbit(x))
        ans+=c[x];
    return ans;
}
void add(ll x,ll y)
{
    for(;x<=n;x+=lowbit(x))
        c[x]+=y;
}
ll sum_ans()
{
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ans+=l[i]*r[i];
    }
    return ans;
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        max_val=max(max_val,a[i]);
    }
    for(ll i=n;i;i--)
    {
        r[i]=ask(max_val)-ask(a[i]);
        add(a[i],1);
    }
    memset(c,0,sizeof(c));
    for(ll i=1;i<=n;i++)
    {
        l[i]=ask(max_val)-ask(a[i]);
        add(a[i],1);
    }
    ll sum1=sum_ans();
    memset(c,0,sizeof(c));
    for(ll i=n;i;i--)
    {
        r[i]=ask(a[i]-1);
        add(a[i],1);
    }
    memset(c,0,sizeof(c));
    for(ll i=1;i<=n;i++)
    {
        l[i]=ask(a[i]-1);
        add(a[i],1);
    }
    ll sum2=sum_ans();
    printf("%lld %lld\n",sum1,sum2);
}

 

posted @ 2019-08-13 11:08  Ldler  Views(230)  Comments(0Edit  收藏  举报