• [模板] 树状数组及其应用


    树状数组


    树状数组又是一个区间查询修改利器 前缀和的维护 差分的维护

    又强又方便

    二进制组合原理

    
    struct bit
    {
    	ll c[MAXN], N; //c树, N数组长
    	
    	bit() {}
    	bit(int n) { N = n; fill(c, c + N + 1, 0);	} //初始化数组长
    	
    	int lowbit(int x) { return x & -x; }
    	
    	void update(int pos, ll val)
    	{
    		for( ;pos <= N; pos += lowbit(pos))
    			c[pos] += val;
    	}
    	
    	ll ask(int pos)
    	{
    		ll ret = 0;
    		for( ;pos; pos -= lowbit(pos))
    			ret += c[pos];
    		return ret;
    	}
    };
    

    维护二维前缀和

    /*
        Zeolim - An AC a day keeps the bug away
    */
    
    //#pragma GCC optimize(2)
    //#pragma GCC ("-W1,--stack=128000000")
    #include <bits/stdc++.h>
    using namespace std;
    #define mp(x, y) make_pair(x, y)
    #define fr(x, y, z) for (int x = y; x < z; ++x)
    #define pb(x) push_back(x)
    #define mem(x, y) memset(x, y, sizeof(x))
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ld;
    typedef std::pair<int, int> pii;
    typedef std::vector<int> vi;
    // typedef __int128 ill;
    const ld PI = acos(-1.0);
    const ld E = exp(1.0);
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    const ll MOD = 386910137;
    const ull P = 13331;
    const int MAXN = 5e3 + 100;
    
    int n, m;
    
    int low(int x) { return x & -x; }
    
    ll c[MAXN][MAXN] = { 0 };
    
    void update(int x, int y, int z) {
        for (; x <= n; x += low(x)) {
            for (int j = y; j <= m; j += low(j)) {
                c[x][j] += z;
            }
        }
    }
    ll ask(int x, int y) {
        ll ret = 0;
        for (; x > 0; x -= low(x)) {
            for (int j = y; j > 0; j -= low(j)) {
                ret += c[x][j];
            }
        }
        return ret;
    }
    
    int main() {
        scanf("%d%d", &n, &m);
    
        int opt, x, y, z;
    
        while (scanf("%d", &opt) == 1) {
            if (opt == 1) {
                scanf("%d%d%d", &x, &y, &z);
                update(x, y, z);
            } else {
                int x1, y1, x2, y2;
                scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
                ll ans = ask(x2, y2) - ask(x1 - 1, y2) - ask(x2, y1 - 1) + ask(x1 - 1, y1 - 1);
                printf("%lld
    ", ans);
            }
        }
    
        return 0;
    }
    
    /*
    3 4
    1 2 3
    1 2 3
    2 3 5
    2 3 7
    */

    用法 1,区间查询和 [fst, lst] 

    前缀和相减即可                          sum = crr[x] - crr[y - 1]

    用法2 单点更新                          add(pos, val)

    用法3 区间加和 [fst, lst]              add(fst, val), add(lst + 1, -val); 差分

    用法4 区间加和的单点查询        ans = ask(k) + arr[k];

    树状数组求逆序数


    树状数组可以求前缀和,

    那么设一个数组C记录当前下标的数的个数

    所以从后往前扫数组可以logn的查询出当前值之后 的 比当前值小的所有数的前缀和(逆序数)

    扫完再把当前值插入二进制下标树即可

    当值太大的时候可以离散 这时候时间复杂度肯定就比归并要大的多了, 不过可以解决归并背不下来的情况

    /*
        Zeolim - An AC a day keeps the bug away
    */
    
    //#pragma GCC optimize(2)
    #include <cstdio>
    #include <iostream>
    #include <cstdlib>
    #include <cmath>
    #include <cctype>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <set>
    #include <sstream>
    #include <map>
    #include <ctime>
    #include <vector>
    #include <fstream>
    #include <list>
    #include <iomanip>
    #include <numeric>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    const int INF = 0x3f3f3f3f;
    const ld PI = acos(-1.0);
    const ld E = exp(1.0);
    const int MAXN = 1e6 + 10;
    
    ll arr[MAXN] = {0};
    ll brr[MAXN] = {0};
    
    ll c[MAXN] = {0};
    
    ll rp(ll x, ll n)
    {
        return lower_bound(brr, brr + n, x) - brr + 1;
    }
    
    ll lobit(ll x)
    {
        return x & -x;
    }
    
    void add(ll x, ll n)
    {
        for(; x <= n; x += lobit(x))
            ++c[x];
    }
    
    ll ask(ll x)
    {
        ll ret = 0;
    
        for( ; x; x -= lobit(x))
            ret += c[x];
    
        return ret;
    }
    
    int main()
    {
        //ios::sync_with_stdio(false);
        //cin.tie(0);     cout.tie(0);
        //freopen("D://test.in", "r", stdin);
        //freopen("D://test.out", "w", stdout);
    
        int n;
    
        cin >> n;
    
        for(int i = 0; i < n; ++i)
        {
            cin >> arr[i];
        }
    
        memcpy(brr, arr, sizeof(arr));
    
        sort(brr, brr + n);
    
        int ln = unique(brr, brr + n) - brr;
    
        ll ans = 0;
    
        for(int i = n - 1; i >= 0; --i)
        {
            int pos = rp(arr[i], ln);
    
            ans += ask(pos - 1);
    
            add(pos, ln);
        }
    
        cout << ans << '
    ';
    
    
    
        
        return 0;
    }
  • 相关阅读:
    乱码解决方案SecureCRT中文乱码解决方案
    普通用户注销windows server 2003 普通用户(users)远程登录立即自动注销的解决方法
    jquery同步基于jquery的$.ajax async使用
    服务解释WinSer 8 无法访问共享官方解释
    备用nulljs 输出内容到新窗口
    返回解释Java乔晓松Android SD卡路径问题以及如何获取SDCard内存大小
    复制最佳实践MySQL 磁盘复制技术DRBD:优缺点比较、注意事项以及最佳实践
    schema类SpringMVC+Hibernate+Spring整合(二)
    类class2013第十四周上机任务【项目2 抽象Shape类】
    数据库javaJAVA连接oracle数据库
  • 原文地址:https://www.cnblogs.com/zeolim/p/12270382.html
Copyright © 2020-2023  润新知