Mar 13th T1 树状数组逆序对的简单性质应用(
Problem 1. rotinv
如果你有一个长度为\(n\)的序列:
\[a_1,a_2,a_3,...a_n
\]
那么它的一个逆序对是一二元组\(<i,j>\)满足\(i<j\)且\(a_i>a_j\),其中\(i,j\in[1,n]\)。
我们称一个序列所包含的逆序对数为这个序列的逆序对数。
那么问题来了:
给出一个长度为n的序列,需要计算
\[a_1,a_2...a_{n-1},a_n
\]
\[a_2,a_3...a_n,a_1
\]
\[a_3,a_4...a_1,a_2
\]
\[...
\]
\[a_n,a_1...a_{n-2},a_{n-1}
\]
这\(n\)个序列的逆序对之和。
\(n≤10^6,1≤a_i≤n\)
Sol
考虑求初始序列\(a_1..a_n\),就是树状数组逆序对板子。
现在考虑把\(a_i\)丢到最后面的影响。
我们令\(f[a[i]]\)为序列\(n\)中\(i,j\)非逆序对的个数(即\(a_i>=a_j\)且\(i>j\))(你的树状数组的\(query\)写的应该就是这东西)
则新增加的逆序对个数显然为\(n-f[a[i]]\)
注意到除了新增加的,也有减少的。
即所有\(j\)使得\(j>1\)(当时\(i\)的位置)且\(a_j<a_i\)所构成的逆序对都不存在了,因为\(i\)被丢到了最后。
换句话说就是所有比\(i\)小的。
如何求减少的?因为\(f[a[i]-1]\).
就这。还在考试写的有点糊,不保证除了自己以外的人能看得懂(((
#include <iostream>
#include <cstdio>
#include <cstring>
#define lb(x) (x&(-x))
#define re register int
#define maxn 1000005
#define ll long long
using namespace std;
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
int a[maxn];
int tr[maxn];
int n;
inline void add(int x){
for(re i=x;i<=n;i+=lb(i)){
tr[i]++;
}
}
inline int query(int x){
int ans=0;
for(re i=x;i;i-=lb(i)){
ans+=tr[i];
}
return ans;
}
void file(){
freopen("rotinv.in","r",stdin);
freopen("rotinv.out","w",stdout);
}
int main(void){
//file();
n=read();
ll L=0;
for(re i=1;i<=n;i++){
a[i]=read();
add(a[i]);
L+=i-query(a[i]);
}
ll ans=0;
for(re i=1;i<=n;i++){
L+=n-query(a[i])-query(a[i]-1);
ans+=L;
}
printf("%lld\n",ans);
return 0;
}