AcWing 241 楼兰图腾

241. 楼兰图腾

在完成了分配任务之后,西部 314来到了楼兰古城的西部。

相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(),他们分别用 V 和  的形状来代表各自部落的图腾。

西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n个点,经测量发现这 nn 个点的水平位置和竖直位置是两两不同的。

西部 314认为这幅壁画所包含的信息与这 nn 个点的相对位置有关,因此不妨设坐标分别为 (1,y1),(2,y2),…,(n,yn),其中 y1∼yn是 1 到 n 的一个排列。

西部 314打算研究这幅壁画中包含着多少个图腾。

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

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

西部 314 想知道,这 nn 个点中两个部落图腾的数目。

因此,你需要编写一个程序来求出 V 的个数和  的个数。

输入格式

第一行一个数 n。

第二行是 n 个数,分别代表 y1,y2,…,yn。

输出格式

两个数,中间用空格隔开,依次为 V 的个数和  的个数。

数据范围

对于所有数据,n≤200000,且输出答案不会超过 int64。
y1∼yn 是 1 到 n 的一个排列。

输入样例:

5
1 5 3 2 4

输出样例:

3 4

思路:树状数组,逆序对。

求v字图腾个数时,先逆序扫描序列a,求出每个a[i]后面有几个数比它大,记为right[i],再正序扫描,求出每个a[i]前面有几个数比它大,记为left[i],则以该点为中心的v字形个数为left[i]*right[i].

求^字图腾个数时,先逆序扫描序列a,求出每个a[i]后面有几个数比它小,极为right[i],再正序扫描序列a,求出每个a[i]前面有几个数比它小,记为left[i],则以该点为中心的^字形个数为left[i]*right[i]

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2000010;
ll n,a[maxn],t[maxn];
ll lower[maxn],gre[maxn];//lower数组记录比它小的数字个数,gre数组记录比它大的数字个数

ll lowbit(ll x){//lowbit运算
    return x&-x;
}
void add(ll x,ll y){//添加
    for(;x<=n;x+=lowbit(x)) t[x]+=y;
}
ll ask(ll x){//查询
    ll ans=0;
    for(;x;x-=lowbit(x)) ans+=t[x];
    return ans;
}
int main(){
    cin>>n;
    for(ll i=1;i<=n;i++){
        cin>>a[i];
    }
    for(ll i=1;i<=n;i++){
        ll y=a[i];
        lower[i]=ask(y-1);
        gre[i]=ask(n)-ask(y);
        add(y,1);//将y加入树状数组,此数字出现1次
    }
    memset(t,0,sizeof(t));//清空树状数组
    ll ans1=0,ans2=0;
    for(ll i=n;i>=1;i--){//逆序扫描
        ll y=a[i];
        ans1+=(ll)lower[i]*ask(y-1);//^形:从后往前枚举时,将比它小的数的个数乘以前面查询的比它小的个数
        ans2+=(ll)gre[i]*(ask(n)-ask(y));//v形:从后往前枚举时,将比它大的个数乘以比它大的个数  
        //若要查询a的区间[l,r]中所有数的和,只需计算ask(r)-aks(l-1)
        add(y,1);//将y加入树状数组,即数字Y出现1次
    }
    cout<<ans1<<" "<<ans2<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值