楼兰图腾
作者:互联网
楼兰图腾
原题链接
在完成了分配任务之后,西部 314 来到了楼兰古城的西部。
相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(\(V\)),一个部落崇拜铁锹(\(∧\)),他们分别用 \(V\) 和 \(∧\) 的形状来代表各自部落的图腾。
西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 \(n\) 个点,经测量发现这 \(n\) 个点的水平位置和竖直位置是两两不同的。
西部 314 认为这幅壁画所包含的信息与这 \(n\) 个点的相对位置有关,因此不妨设坐标分别为 \((1,y_1),(2,y_2),…,(n,y_n)\),其中 \(y_1∼y_n\) 是 1 到 n 的一个排列。
西部 314 打算研究这幅壁画中包含着多少个图腾。
如果三个点 \((i,y_i),(j,y_j),(k,y_k)\) 满足 \(1≤i<j<k≤n\) 且 \(y_i>y_j\), \(y_j<y_k\),则称这三个点构成 V 图腾;
如果三个点 \((i,y_i),(j,y_j),(k,y_k)\) 满足 \(1≤i<j<k≤n\) 且 \(yi<y_j\),\(y_j>y_k\),则称这三个点构成 ∧ 图腾;
西部 314 想知道,这\(n\)个点中两个部落图腾的数目。
因此,你需要编写一个程序来求出 \(V\) 的个数和 \(∧\) 的个数。
题意解读与思路
题目很长,但是把那些背景剥开发现其实题目就是想让我们快速地处理一个数组的逆序对,我们在归并排序那里学过一种方法处理求逆序对数量的问题,这里我想介绍一种更加易于理解的方法来处理这个问题。
首先,我们可以发现数的范围不大仅是只有1到\(n\),最大不超过\(2e5\),那么我们考虑是不是可以在处理每个数的时候,把这个数直接放进对应下标的数组中,然后直接求\(a_i\)到\(n\)有多少个数。那么我们就需要一个方法去达到快速快速修改数组中的一个数,并且能够快速求出前缀和。
那么,我们不难想到树状数组和线段树可以用来处理这个问题。
对于\(V\)我们只需要先从1到\(n\)求一边比\(a_i\)大的值再反过来再求一遍然后两边相乘即可。\(∧\)也是一样。
#include <iostream>
#include <cstring>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int tr[N], w[N];
int g[N], l[N];
int n;
inline int lowbit(int x){return x & -x;}
void add(int x, int v){
while(x <= n){
tr[x] += v;
x += lowbit(x);
}
}
int sum(int x){
int res = 0;
while(x){
res += tr[x];
x -= lowbit(x);
}
return res;
}
signed main(){
scanf("%lld", &n);
for(int i = 1; i <= n; i++)scanf("%lld", &w[i]);
for(int i = 1; i <= n; i++){
int x = w[i];
g[i] = sum(n) - sum(x);
l[i] = sum(x - 1);
add(x, 1);
}
memset(tr, 0, sizeof tr);
int ans1 = 0, ans2 = 0;
for(int i = n; i; i--){
int x = w[i];
ans1 += g[i] * (sum(n) - sum(x));
ans2 += l[i] * (sum(x - 1));
add(x, 1);
}
printf("%lld %lld\n", ans1, ans2);
return 0;
}
标签:个点,图腾,int,314,西部,楼兰 来源: https://www.cnblogs.com/jokeBlog/p/15327417.html