树状数组求逆序对的应用(数据非离散化)

链接:https://ac.nowcoder.com/acm/contest/1032/A
来源:牛客网

题目描述

在完成了分配任务之后,西部314来到了楼兰古城的西部。
相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(‘V’),一个部落崇拜铁锹(‘∧’),他们分别用V和∧的形状来代表各自部落的图腾。
西部314在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了N个点,经测量发现这N个点的水平位置和竖直位置是两两不同的。
西部314认为这幅壁画所包含的信息与这N个点的相对位置有关,因此不妨设坐标分别为(1,y1),(2,y2),…,(n,yn)(1,y_1),(2,y_2),…,(n,y_n)(1,y1),(2,y2),,(n,yn),其中y1∼yny_1\sim y_ny1yn是1到n的一个排列。
西部314打算研究这幅壁画中包含着多少个图腾。
如果三个点(i,yi)(i,y_i)(i,yi),(j,yj)(j,y_j)(j,yj),(k,yk)(k,y_k)(k,yk)满足1≤i<j<k≤n1\leq i \lt j \lt k \leq n1i<j<kn且yi>yjy_i>y_jyi>yj,yj<yky_j \lt y_kyj<yk,则称这三个点构成V图腾;
如果三个点(i,yi)(i,y_i)(i,yi),(j,yj)(j,y_j)(j,yj),(k,yk)(k,y_k)(k,yk)满足1≤i<j<k≤n1\leq i \lt j \lt k\leq n1i<j<kn且yi<yjy_i<y_jyi<yj,yj>yky_j \gt y_kyj>yk,则称这三个点构成∧图腾;
西部314想知道,这n个点中两个部落图腾的数目。
因此,你需要编写一个程序来求出V的个数和∧的个数。


输入描述:

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

输出描述:

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

输入

5
1 5 3 2 4

输出

3 4

备注:

对于所有数据,n≤200000n \leq 200000n200000,且输出答案不会超过int64。

1)用树状数组求逆序对就是将数值arr[i]直接当成下标处理,在利用树状数组t[arr[i]]++,
2)需要注意的是AC代码只用树状数组求在arr[i]之前或者之后的值比arr[i]小的个数,比它大的直接能减出来,详情看代码
3)还有最后right[i]*left[i]时会超出int范围,所以要强制转换成long long

AC代码如下:
 1 #include <cstdio>
 2 #define LL long long
 3 using namespace std;
 4  
 5 const int N=200000;
 6 int n,arr[N+5];
 7 int t[N+5];
 8 int right[N+5],left[N+5];
 9  
10 void Init(){
11     for(int i=0;i<=n;i++)
12         t[i]=0;
13 }
14 int lowbit(int x){
15     return x&(-x);
16 }
17 void add(int idx){
18     while(idx<=n){
19         t[idx]+=1;
20         idx+=lowbit(idx);
21     }
22 }
23 void query(int i,int idx,int *p){
24     int res=0;
25     int temp=i;
26     while(idx){
27         res+=t[idx];
28         idx-=lowbit(idx);
29     }
30     p[i]=res;
31 }
32  
33 int main(void)
34 {
35     scanf("%d",&n);
36  
37     for(int i=1;i<=n;i++)
38         scanf("%d",&arr[i]);
39  
40     for(int i=n;i>0;i--){
41         add(arr[i]);//把数字本身当成下标
42         query(i,arr[i]-1,right);
43     }
44     Init();
45     for(int i=1;i<=n;i++){
46         add(arr[i]);
47         query(i,arr[i]-1,left);
48     }
49     LL sum,cnt;
50     sum = cnt = 0;
51     for(int i=1;i<=n;i++){
52         cnt+=(LL)(i-1-left[i])*(n-i-right[i]);
53         sum+=(LL)right[i]*left[i];
54     }
55     printf("%lld %lld\n",cnt,sum);
56  
57     return 0;
58 }
 

猜你喜欢

转载自www.cnblogs.com/gn1314/p/11544030.html