AcWing 241. 楼兰图腾--树状数组

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

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

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

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

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

如果三个点 ( i , y i ) , ( j , y j ) , ( k , y k ) (i,y_i),(j,y_j),(k,y_k) (i,yi),(j,yj),(k,yk) 满足 1 ≤ i < j < k ≤ n 1≤i<j<k≤n 1i<j<kn y i > y j , y j < y k y_i>y_j,y_j<y_k yi>yj,yj<yk ,则称这三个点构成 V 图腾;

如果三个点 ( i , y i ) , ( j , y j ) , ( k , y k ) (i,y_i),(j,y_j),(k,y_k) (i,yi),(j,yj),(k,yk) 满足 1 ≤ i < j < k ≤ n 1≤i<j<k≤n 1i<j<kn y i < y j , y j > y k y_i<y_j,y_j>y_k yi<yj,yj>yk ,则称这三个点构成 图腾;

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

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

输入格式

第一行一个数 n。

第二行是 n 个数,分别代表 y 1 , y 2 , … , y n y1,y2,…,yn y1y2,,yn

输出格式

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

数据范围

对于所有数据, n ≤ 2 ∗ 1 0 5 n≤2*10^5 n2105,且输出答案不会超过 int64。
y 1 ∼ y n y1∼yn y1yn 是 1 到 n 的一个排列。

输入样例:
5
1 5 3 2 4
输出样例:
3 4
#include<bits/stdc++.h>
#include <unordered_map>
using namespace std;
template<class...Args>
void debug(Args... args) {//Parameter pack
    auto tmp = { (cout << args << ' ', 0)... };
    cout << "\n";
}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>pll;
typedef pair<int, int>pii;
const ll N = 1e6 + 5;
const ll MOD = 1e9 + 7;
const ll INF = 0x7fffffff;

const ll maxv = N;//区间最大值
ll Binary_Indexed_Tree[N];//树状数组
ll lowbit(ll x) {//数状数组lowbit()
    return x & -x;
}
void update(ll index, ll val) {//在index位置加上val
    while (index <= maxv) {
        Binary_Indexed_Tree[index] += val;
        index += lowbit(index);
    }
}
ll sum(ll index) {//求Binary_Indexed_Tree[1 , index]的和
    ll ans = 0;
    while (index) {
        ans += Binary_Indexed_Tree[index];
        index -= lowbit(index);
    }
    return ans;
}

ll a[N];
ll L_big[N], L_small[N], R_big[N], R_small[N];
int main() {
    ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];

    for (int i = 1; i <= n; i++) {//计算左区间
        //顺序遍历,已加入树状数组的点必然出现在现加入的点的左边的位置
        //加入的是对应位置的值,所以在树状数组中左边是小于他的元素右边是大于它的元素
        L_small[i] = sum(a[i]);
        L_big[i] = sum(n) - sum(a[i]);
        update(a[i], 1);
    }

    memset(Binary_Indexed_Tree, 0, sizeof(Binary_Indexed_Tree));

    for (int i = n; i >= 1; i--) {//计算右区间
        //逆序遍历,已加入树状数组的点必然出现在现加入的点的右边的位置
        //加入的是对应位置的值,所以在树状数组中左边是小于他的元素右边是大于它的元素
        R_small[i] = (sum(n) - sum(a[i]));
        R_big[i] = sum(a[i]);
        update(a[i], 1);
    }

    ll ansA = 0, ansV = 0;
    for (int i = 1; i <= n; i++) {
        //以某j为基础j左边所有比他大的数乘以它右边比他小的数为V的个数
        ansV += L_big[i] * R_small[i];
        ansA += L_small[i] * R_big[i];
    }
    cout << ansV << " " << ansA;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值