1. 程式人生 > 其它 >樹狀陣列——在高階字首和上的應用

樹狀陣列——在高階字首和上的應用

HDU中超第五場打完後補題,被一個高階字首和折磨了兩天 記錄一下想法

HDU中超第五場打完後補題,被一個高階字首和折磨了兩天
記錄一下想法

樹狀陣列——高階字首和

首先強化一下概念:樹狀陣列的基本(根本)操作是單點修改,單點查詢字首和

通過對字首和做小學減法我們做到了區間查詢

現在要求對 \(a\)​​ 陣列做單點修改,查詢 \(a\)​​ 陣列的二維字首和

\(b,c\) 兩陣列分別為一階、二階字首和

\[\begin{align} b_i &= \sum_1^i a_i,\quad c_i = \sum_1^ib_i\\ c_i &= ia_1 + (i-1)a_2+...+2a_{i-1} + a_i\\ &= i(a_1+a_2+...+a_i) - [(i-1)a_i + (i-2)a_{i-1}+....+a2+0] \end{align} \]

這樣就將一個二階字首和問題轉化成了兩個一階字首和問題

用兩個樹狀陣列,分別處理 \(a_i\)​​​ ,\({(i-1)a_i}\)​​ 這兩個序列即可

struct BitTree{//4行樹狀陣列
    ll t[2*maxn];
    ll lb(int x) {return x&-x;}
    void add(int p, ll ad){for(int i=p;i<=n;i+=lb(i)) t[i]+=ad;}
    ll sum(int p){ return p?t[p] + sum(p-lb(p)):0;}
};

struct BitTree2{//二階字首和的樹狀陣列
    BitTree bt1, bt2;
    void add(int p, ll ad){
        bt1.add(p, ad);
        bt2.add(p, ad * (p-1));
    }
    ll sum(int p){
        return bt1.sum(p) * p - bt2.sum(p);
    }
};

練習(模板題)

線段樹最基礎的模板題,區間修改區間查詢,可以用二階字首和的樹狀陣列做

具體而言,建立差分陣列 \(d\)​ ,區間修改可以改為在 \(d\)​ 上的單點修改,那麼區間查詢就相當於查詢 \(d\)​ 的二階字首和

BitTree2 bt;
ll d[maxn], n, m;

void solve(){
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        cin >> d[i];
        bt.add(i, d[i]);
        bt.add(i+1, -d[i]);
    }
    for(int i=1;i<=m;i++){
        int opt,x,y,k;
        cin >> opt >> x >> y;
        if(opt == 1){
            cin >> k;
            bt.add(x, k);
            bt.add(y+1, -k);
        }else{
            cout << bt.sum(y) - bt.sum(x-1) << '\n';
        }
    }
}

更高階呢?

以三階字首和為例

\[\begin{align} b_i &= \sum_1^i a_i, \quad c_i = \sum_1^ib_i, \quad d_i = \sum_1^ic_i \\ d_k &= \frac{k(1+k)}{2}a_1 + \frac{(k-1)k}{2}a_2+...+\frac{(2+k-i)*(1+k-i)}{2}a_i+...+\frac{3}{2}a_{k-1} + a_k \\ &= \frac{(k+1)(k+2)}{2}(a_1+...a_k)-\frac{2k+3}{2}(a_1+2a_2+...+ka_k)+\frac{1}{2}(a_1+4a_2+...+k^2a_k) \end{align} \]

推方程的時候,對於一般項 \(\frac{(2+k-i)*(1+k-i)}{2}a_i\)​​,要注意把 \(k\)\(i\) ​分開,目的是使某個\(f(i)a_i\) 形式的東西獨立出來,方便單獨用樹狀陣列維護。

這裡的過程是:

\[\frac{(2+k-i)*(1+k-i)}{2}a_i = \frac{(k+1)(k+2)}{2}a_i-\frac{2k+3}{2}ia_i+\frac{1}{2}i^2a_i \]

由此也成功轉化成三個一階字首和問題

如果繼續推下去,會發現 \(n\) 個樹狀陣列就可以解決 \(n\) 階字首和問題

練習題(比賽原題)

三階字首和

題解見自己寫的 2021暑假 HDU中超 第五場 1009