逆序對(樹狀陣列)題解
逆序對題解出處(1266)
老實說,還沒有歸併排序快
先上程式碼,再解釋
#include<cstdio> #include<algorithm> using namespace std; inline void read(long long &x) { x=0; long long f=1; char s=getchar(); while(s<'0'||s>'9') { if(s=='-')f=-1; s=getchar(); } while(s>='0'&&s<='9') { x=x*10+s-48; s=getchar(); } x*=f; } inline void pr(long long x) { if(x<0)x=-x; if(x>9)pr(x/10); putchar(x%10+48); } struct node { long long num,id; } a[500005]; long long lsh[500005],c[500005],b[500005],n,k,ans; inline int lowbit(int x) { return x&-x; } inline void update(int x,int k) { for(int i=x; i<=n; i+=lowbit(i)) c[i]+=k; } inline int sum(int x) { int ans=0; for(int i=x; i>0; i-=lowbit(i)) ans+=c[i]; return ans; } inline bool cmp(node a,node b) { return a.num<b.num; } int main() { read(n); for(int i=1; i<=n; i++) read(a[i].num),a[i].id=i; sort(a+1,a+1+n,cmp); int cnt=0; for(int i=1; i<=n; i++) { if(a[i].num!=a[i-1].num) cnt++; lsh[a[i].id]=cnt; } for(int i=1; i<=n; i++) { update(lsh[i],1); ans+=i-sum(lsh[i]); } pr(ans); }
現在我們來分段解釋(快讀快輸不解釋)
先來看樹狀陣列是個啥
大概就是這麼個玩意兒,有什麼用後面再解釋
然後來看lowbit:
lowbit:
lowbit(i)的意思是將 i 轉化成二進位制數之後,只保留最低位的1及其後面的0,截斷前面的內容,然後再轉成十進位制數,這個數也是樹狀陣列中i號位的子葉個數。比如lowbit(22)的意思是將 22 轉化成二進位制數之後,得到10110,保留末位的1及其後的0,並截斷前面的內容,得到10,轉化為十進位制數為2,即lowbit(22)=2,證明C[22]的子葉數為
1、求lowbit方法一:
原數為x(十進位制),先將原數轉化成二進位制之後的最後一位1替換成0,然後再用原數減去替換掉最後一位1後的數(十進位制相減),答案就是lowbit(i)的結果;
lowbit(i)
{
return i - ( i & ( i – 1 ) );
}
說明:
i-1的二進位制可以看做A0C(C是和B一樣長的1)
i & (i - 1)的二進位制就是A1B & A0C = A0B
i – (i & (i - 1))的二進位制就是A1B – A0B = 0…010…0
2、求lowbit方法二:
原數為i(十進位制),先將原數轉化成二進位制之後,在與原數相反數的二進位制按位與,答案就是lowbit(i)的結果;
lowbit(i)
{
return i & -i;
}
例如:lowbit(22)=2
22的二進位制原碼011010,正數的補碼等於它的原碼011010
-22的二進位制原碼111010,負數的補碼等於它的原碼取反加1,為100110
011010 & 100110 = 000010 正數轉換成原碼後依然是000010
所以lowbit(22)=2
不懂自己百度
根據這個特點
void update(int k,int x)
{
for(int i = k; i <= n; i += lowbit(i))
C[i] += x;
}
結合圖片,可以求出c陣列的值。
然後來看如何求字首和:
求字首和B[]段程式碼 (PS:此區間為字首和,也就是1~i)
int Sum(int k)
{
for(int i = k; i > 0; i -= lowbit(i) )
B[k] += C[i];
return B[k];
}
自己結合影象理解
逆序對其實就是求前面有幾個比他大的數,然後把每一個的和累加起來就好了
程式碼分析:
int main() {
read(n);
for(int i=1; i<=n; i++)
read(a[i].num),a[i].id=i;
sort(a+1,a+1+n,cmp);
int cnt=0;
for(int i=1; i<=n; i++) {
if(a[i].num!=a[i-1].num)//考慮重複的情況,這裡可以用STL+二分離散化
cnt++;
lsh[a[i].id]=cnt;//離散化可以使資料很大時將陣列開小
}
for(int i=1; i<=n; i++) {
update(lsh[i],1);
ans+=i-sum(lsh[i]);
}
pr(ans);
}
在最後為什麼要用ans+=i-sum(lsh[i])留給大家自己思考