1. 程式人生 > 其它 >linux高效能伺服器程式設計---第五章Linux網路程式設計基礎API (2)

linux高效能伺服器程式設計---第五章Linux網路程式設計基礎API (2)


  1. 參考:

    1. 演算法訓練營第二章

  2.  

    1. 名稱:分塊。

    2. 本質:優化後的暴力。

    3. 一些abstract:

      設sn = 根號n。

      樹狀陣列和線段樹維護的資訊必須滿足資訊合併特性,即對於區間來說,分成兩個區間分別操作和單獨對一個區間的操作是一樣的。不滿足這個特性,則不能使用樹狀陣列和線段樹。

      分塊幾乎可以解決所有區間更新和區間查詢問題,但是效率差一些。分塊演算法就是把所有資料分成若干塊,維護塊內資訊,使得塊內查詢為O(1)時間,總查詢為O(sn)。

      設資料是連續的n個,通常我們將資料分成sn塊,前面的塊大小都是sn,最後一塊可能不足這個值,也無所謂。pos[i]表示位置i所屬的塊號。對每個塊都進行資訊維護(暴力/懶標記)(懶標記用來存整個區間都進行的操作,只有不是整個區間的操作時才處理懶標記),分塊可以解決如下問題:

      1. 單點更新:所屬區間懶標記下傳,暴力更新,複雜度O(sn)。

      2. 區間更新:對於待修區間橫跨的整個塊,打上懶標記即可,顯然只有兩側的塊可能打不了,則下傳二者懶標記,再暴力修改覆蓋部分的值。因為整個塊的數量不超過sn,並且暴力修改的部分的長度小於2sn,下傳懶標記也是暴力改,所以下傳的運算元小於2sn,下傳完再直接修改不完整的部分,運算元也小於2sn,所以總共要進行的操作小於5sn,複雜度O(sn)。

      3. 區間查詢:和區間更新類似,也是整個塊的用塊儲存的資訊統計答案,兩端不完整的塊就暴力掃描,時間複雜度同上,為O(sn)。

  3. 操作:

    1. 預處理:將序列分塊,每個塊都標記左右端點的下標,存到L[i],R[i]中,對最後一個塊,要單獨賦值它的R[i]。分塊結果如下:

      R[4] = n = 10

       int = (int)(sqrt(1.0)), num t;
       if (num) num++;  // 一共n個,分成了num塊,每一個塊大小為t
       for (int 1; <= num; i++) {
         L[i] = (i-1)*1;  // 第i塊的起點
         R[i] t;  // 第i塊的終點
       }
       R[num] n;  // 最後一塊越界了,需要改回來

      這裡以維護區間和為例:

       // sum[i]表示第i塊的和,a存原始資料,pos[i]表示第i個數據屬於pos[i]塊
       
       for (int 1; <= num; i++) {
         for (int L[i]; <= R[i]; j++) {
           pos[j] i;
           sum[i] += a[j];
        }
       }
    2. 區間更新:比如將區間[l, r]區間的元素都加d(假設r不小於l)。

      先求出l和r所屬的塊:

       int pos[l], pos[r];

      分類討論:如果同屬一塊,則直接暴力修改:(這裡因為只有加,懶標記只有一種,所以不用先下放懶標記,不然先後順序需要注意一下,比如有一個乘法的懶標記,再進行加操作,就得先下放乘法的懶標記,再執行這次的加法)

       if (== q) {
         for (int l; i<= r; i++) {
           a[i] += d;
        }
         sum[p] += 1ll * (1);
       }

      不屬於同一塊,則對中間完全覆蓋的塊打上懶標記,暴力修改不完整的兩個小塊:

       else {
         add[i] += d;
         for (int 1; <= 1; i++) {  
           add[i] += d;  // 對中間完全覆蓋的塊打上懶標記
        }
         // 暴力修改不完整的兩個小塊
         for (int l; <= R[p]; i++) {
           a[i] += d;
        }
         sum[p] += 1ll * (R[p] 1);
         for (int L[q]; <= r; i++) {
           a[i] += d;
        }
         sum[q] += 1ll * (L[q] 1);
       }
    3. 區間查詢:查詢[l, r]區間的元素和。

      先求出l和r所屬的塊:

       int pos[l], pos[r];
       long long ans 0;

      分類討論:如果同屬一塊,則直接暴力累加,再加上懶標記上的值。

       if (== q) {
         for (int l; i<= r; i++) {
         ans += a[i];
        }
         ans += 1ll add[p] * (1);
       }

      不屬於同一塊,則對中間完全覆蓋的塊打上懶標記,暴力修改不完整的兩個小塊:

       else {
         for (int 1; <= 1; i++) {  
           ans += sum[i] 1ll add[i] * (R[i] L[i] 1);  // 對中間完全覆蓋的塊打上懶標記
        }
         // 暴力修改不完整的兩個小塊
         for (int l; <= R[p]; i++) {
           ans += a[i];
        }
        ans += 1ll add[p] * (R[p] 1);
         for (int L[q]; <= r; i++) {
           ans += a[i];
        }
         sum[q] += 1ll add[q] * (L[q] 1);
       }
       
       printf("%lld", ans);
  4. 例題:

    1. (HDU5057)給出一個數組,有兩個操作:

      1. 單點修改某個位置的值

      2. 區間查詢[l, r],指出這個區間中從低往高數第p位為q的個數是多少

      給n個數,m個操作(都是十萬級別)。對於每個查詢,給出答案。

      考慮分塊,先預處理最開始的每個區間的每個位的不同的值的數量,這個複雜度是O(10nlog(int))級別的。

      對於查詢操作,只需要O(sn)級別。

      對於修改操作,暴力修改即可,是O(1)級別。

      輕鬆通過。

       #define maxn 100010
       #define sn 333
       // block[i][j][k]表示第i塊中第j位有多少個為k的數。
       int a[maxn], pos[maxn], L[sn], R[sn], block[sn][11][10], n, m;
       int ten[11] = {0,1, 10, 100, 1000, 10000, 100000, 100000, 1000000, 10000000, 100000000};  // ten[i]就是第i位需要這個數需要除以多少才能取到
       
       void pre(int n) {
         int = (int)(sqrt(1.0)), num t;
         if (num) num++;
         for (int 1; <= num; i++) {
           L[i] = (1)*1;
           R[i] t;
        }
         R[num] n;
         for (int 1; <= num; i++) {
           for (int L[i]; <= R[i]; j++) {
             pos[j] i;
          }
           int tmp a[i];
           for (int 1; <= 10; j++) {
             block[belong[i]][j][tmp%10]++;
             tmp /= 10;
          }
        }
       }
       
       void update(int x, int v) {  // a[x] = v
         for (int 1; <= 10; i++) {  // 少了一個原來的a[x],退回去
           block[pos[x]][i][a[x]%10]--;
           a[x] /= 10;
        }
         a[x] v;  // 把v的補上來
         for (int 1; <= 10; i++) {
           block[pos[x]][i][v%10]++;
           /= 10;
        }
       }
       
       int query(int l, int r, int p,