珂朵莉的数列 (离散化+树状)
题意 :
范围: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 }

浙公网安备 33010602011771号