1. 程式人生 > 其它 >C# 呼叫微信群發介面

C# 呼叫微信群發介面

樹狀陣列

基本應用

1.求字首和
2.單點修改
即:動態維護字首和

例題一

PROCESS1: 題目連結

題目大意:求形如“山峰”或者“山谷”狀三元組的數量

PROCESS2:思路

  1. 從暴力出發\(O(N^2)\)

我們可以預處理出來每個點左側和右側大於和小於它的點的數量,然後相乘即可(乘法原理)

  1. 樹狀陣列\(O(Nlog_2^N)\)

樹狀陣列中一種常用的求小於或者與某一個點的數量的方法:將原陣列的權值對映到該點在樹狀陣列的下標(經常與離散化結合使用)。例如\(tr[3]=5\)就表示權值為\(3\)的點有\(5\)個。這樣在樹狀陣列中求小於某一個數y的數的數量,就可以直接求\(y\)

之前的字首和,這個字首和就代表所有小於\(y\)的數的個數和。
但這樣就無法同時預處理一個點左側小於它的點的數量和右側小於它的點的數量,所以我們需要操作兩側,第一次預處理左側的,然後第二次直接求結果即可。

注意答案可能爆int

PROCESS3:程式碼

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 200010;

int n, a[N], g[N], l[N];
int tr[N];

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

void add(int x, int c)
{
    for(int i = x; i <= n; i += lowbit(i))  tr[i] += c;
}

int get(int x)
{
    int res = 0;
    for(int i = x; i; i -= lowbit(i))  res += tr[i];
    return res;
}


int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++ )   cin >> a[i];
    
    for(int i = 1; i <= n; i ++ )
    {
        int y = a[i];
        g[i] = get(n) - get(y);
        l[i] = get(y); 
        add(y, 1);
    }
    
    memset(tr, 0, sizeof tr);
    
    long long res1 = 0, res2 = 0;
    for(int i = n; i >= 1; i -- )
    {
        int y = a[i];
        res1 += (long long)(get(n) - get(y)) * g[i];//這裡會爆int
        res2 += (long long)get(y) * l[i];
        add(y, 1);
    }
    
    cout << res1 << ' ' << res2 << endl;
    
    return 0;
}

拓展

  1. 差分
  2. 差分+公式