珂朵莉的数列 (离散化+树状)

题意 :

 

范围:n<=106,0<=ai<=109

题解:

        1.转换角度:子区间有近n*n个,直接求每个区间内的逆序对个数会超时,不如试着求每对逆序对(a[i],a[j])在多少个区间内,并且我们可以推出这个数量=i*(n-j+1)

        2.树状数组:直接枚举求一个序列中所有的逆序对,再计算每对的数量,复杂度O(n2),而树状数组只能求出每个数有多少对逆序对,但是根据上面的规律可以得到每个数在所有区间各自逆序对的总数 =(sum(n)-sum(j))*(n-j+1),树状数组每个值不再只是记录该元素是否存在,而是每个元素可以存在在多少个区间内,tree[i]=1 —》tree[i]=i;复杂度O(n*log(n))---n次询问

        3.离散化:树状数组需要直接利用每个元素作为下标来记录数据,但是该题的元素范围已经超过了数组可以表示的空间范围,所以需要离散化,将109的数缩小到105之内,O(log(n))

        4.注意:答案可能超过109*109*109,需要用到__int128.

Code:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<vector>
 6 using namespace std;
 7 typedef __int128 ll;
 8 #define lowbit(x) (x&(-x))
 9 int a[1100000],L[1100000],cnt,cntt;
10 ll tree[1100000];
11 void add(int x,int d){
12      while(x<=cntt){
13         tree[x]+=d;
14         x+=lowbit(x);
15      }
16 }
17 ll sum(int x){
18     ll ans=0;
19     while(x){
20         ans+=tree[x];
21         x-=lowbit(x);
22     }
23     return ans;
24 }
25 inline void write(__int128 x)
26 {
27     if(x<0)
28     {
29         putchar('-');
30         x=-x;
31     }
32     if(x>9)
33         write(x/10);
34     putchar(x%10+'0');
35 }
36 int main(){
37      int n;
38      while(~scanf("%d",&n)){
39          cnt=0,cntt=0;ll ans=0;
40          memset(tree,0,sizeof(tree));
41         for(int i=1;i<=n;i++){
42             scanf("%d",&a[i]);
43             L[++cnt]=a[i];
44         }
45         sort(L+1,L+1+cnt);
46         for(int i=1;i<=cnt;i++)
47             if(cntt==0||L[i]!=L[cntt])L[++cntt]=L[i];
48         for(int i=1;i<=n;i++){
49             int tem=lower_bound(L+1,L+1+cntt,a[i])-L;
50             add(tem,i);
51             ans+=(sum(cntt)-sum(tem))*(n-i+1);
52         }
53         write(ans);
54      }
55      return 0;
56 }
View Code

 

 

posted @ 2020-10-10 09:43  十分的月亮  阅读(162)  评论(0)    收藏  举报