树状数组 在线处理序列中比k大的数量 T3 241. 楼兰图腾 - AcWing题库

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

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

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

西部 认为这幅壁画所包含的信息与这 个点的相对位置有关,因此不妨设坐标分别为 ,其中 的一个排列。

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

如果三个点 满足 ,则称这三个点构成 V 图腾;

如果三个点 满足 ,则称这三个点构成  图腾;

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

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

输入格式

第一行一个数

第二行是 个数,分别代表

输出格式

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

数据范围

对于所有数据,,且输出答案不会超过
的一个排列。

输入样例:

5
1 5 3 2 4

输出样例:

3 4

思路

求一个序列中所有满足以下条件的数对个数:

如果三个点 满足 ,则称这三个点构成 V 图腾;

如果三个点 满足 ,则称这三个点构成  图腾;

且数据范围限制只能用 的算法求解, 这形式的问题通常都以中间点为依据来思考。

把所有三个点构成的数对以中间节点的横坐标为依据划分集合。则以 为中间点的 V 图腾, 其数量就是 1k大于k高度的点数量 * k+1n大于k高度的点数量。另一种图腾也同理。

故现在需要做的就是 查询1~k-1中 比 k 大的数量, 和 插入当前k值。区间查询和点修改。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 2e5 + 10;
typedef long long LL;
int a[N], t[N];
int n;
int great[N], low[N];
 
int lowbit(int x)
{
    return x & -x;
}
 
void add(int x, int c)
{
    for(int i = x; i <= n; i+= lowbit(i)) t[i] += c; 
}
 
LL sum(int x)
{
    LL res = 0;
    for(int i = x; i; i -= lowbit(i)) res += t[i];
    return res;
}
 
 
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    
    for(int i = 1; i <= n; i++)
    {
        int y = a[i];
        great[i] = sum(n) - sum(y);
        low[i] = sum(y - 1);
        add(a[i], 1);
    }
    
    memset(t, 0, sizeof t);
    LL res1 = 0, res2 = 0;
    for(int i = n; i; i--)
    {
        int y = a[i];
        res1 += great[i] * (sum(n) - sum(y));
        res2 += low[i] * sum(y - 1);
        add(y, 1);
    }
    
    cout << res1 << " " << res2 << endl;
    
    return 0;
}