1. 程式人生 > >樹狀陣列與線段樹(二)

樹狀陣列與線段樹(二)

樹狀陣列

1.小朋友排隊

n 個小朋友站成一排。

現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。

每個小朋友都有一個不高興的程度。

開始的時候,所有小朋友的不高興程度都是 0。

如果某個小朋友第一次被要求交換,則他的不高興程度增加 1,如果第二次要求他交換,則他的不高興程度增加 2(即不高興程度為 3),依次類推。當要求某個小朋友第 k 次交換時,他的不高興程度增加 k。

請問,要讓所有小朋友按從低到高排隊,他們的不高興程度之和最小是多少。

如果有兩個小朋友身高一樣,則他們誰站在誰前面是沒有關係的。

輸入格式

輸入的第一行包含一個整數 n,表示小朋友的個數。

第二行包含 nn 個整數 H1,H2,…,Hn,分別表示每個小朋友的身高。

輸出格式

輸出一行,包含一個整數,表示小朋友的不高興程度和的最小值。

資料範圍

1≤n≤100000
0≤Hi≤1000000

輸入樣例:

3
3 2 1

輸出樣例:

9

樣例解釋

首先交換身高為3和2的小朋友,再交換身高為3和1的小朋友,再交換身高為2和1的小朋友,每個小朋友的不高興程度都是3,總和為9。

 

解題思路:解題關鍵就是找到最少的交換次數,我們可能會想到氣泡排序法,他就是通過不斷的交換排序來實現的,因此我們可以大膽的假設一些規律,最少的交換次數==逆序對的個數。

對於氣泡排序來說,每次交換最多減少一個逆序數

假設陣列有k個逆序對,

①至少需要交換k次,因此次數>=k

②在氣泡排序中,每次交換(Ai,Ai+1)且Ai>Ai+1,因此必然會使逆序數減1

綜上我們得出最少的交換次數==逆序對的個數

接下來,我們要求每一個小朋友的逆序數,然後根據前n項和公式求出他的不高興數,在求解單個逆序數時,我們就用到樹狀陣列來統計在該陣列中,前方大於他的數和後方小於他的數,總和即為該數的逆序數。

程式碼:

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1000010;
int tr[N],h[N];
ll con[N];
int n;

int lowbit(int x)
{
    return x&-x;
}

void add(int x,int v)
{
    for(int i=x;i<N;i+=lowbit(i))
        tr[i]+=v;
}
int query(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=tr[i];
    return ans;
}
int main()
{
    int i,j;
    cin>>n;
    for(i=0;i<n;i++)
    {
        cin>>h[i];
        h[i]++;
    }
    //計算前面比他大的數
    long long ans=0;
    for(i=0;i<n;i++)
    {
        con[i]=query(N-1)-query(h[i]);
        add(h[i],1);
    }
    //計算後面比它小的數
    memset(tr,0,sizeof(tr));
    for(i=n-1;i>=0;i--)
    {
        con[i]+=query(h[i]-1);
        add(h[i],1);
    }
    //總和
    for(i=0;i<n;i++)
        ans+=con[i]*(con[i]+1)/2;
    cout<<ans;
    return 0;
}

差分

1.差分

輸入一個長度為n的整數序列。

接下來輸入m個操作,每個操作包含三個整數l, r, c,表示將序列中[l, r]之間的每個數加上c。

請你輸出進行完所有操作後的序列。

輸入格式

第一行包含兩個整數n和m。

第二行包含n個整數,表示整數序列。

接下來m行,每行包含三個整數l,r,c,表示一個操作。

輸出格式

共一行,包含n個整數,表示最終序列。

資料範圍

1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整數序列中元素的值≤1000

解題思路:

給定a[1],a[2],...a[n]構造查分陣列b[N],使得

a[i]=b[1]+b[2]+...b[i]

核心操作:將a[L~R]全部加上C,等價於b[L]+=C,b[R+1]-=C

1.a[1~L-1]無影響

2.a[L~R]加上了C

3.a[R+1~N]無影響

#include<iostream>
using namespace std;
const int N=100010;
int b[N],q[N];
void insert(int l,int r,int c)
{
    b[l]+=c;
    b[r+1]-=c;
}
int main()
{
    int n,i,j,m,l,r,c;
    cin>>n>>m;
    for(i=1;i<=n;i++)
        cin>>q[i];
    for(i=1;i<=n;i++)
        b[i]=q[i]-q[i-1];
    while(m--)
    {
        cin>>l>>r>>c;
        insert(l,r,c);
    }
    for(i=1;i<=n;i++)
    {
        q[i]=q[i-1]+b[i];
        cout<<q[i]<<" ";
    }
    return 0;

}

&n