• CodeForces 390E Inna and Large Sweet Matrix(树状数组改段求段)


    树状数组仅仅能实现线段树区间改动和区间查询的功能,能够取代不须要lazy tag的线段树。且代码量和常数较小

    首先定义一个数组 int c[N]; 并清空 memset(c, 0, sizeof c);

    1、单点改动 : c[x] += y; 相应的函数是 change(x, y);

    2、求前缀和 :  相应的函数是 int sum(x)

    两种操作的复杂度都是O(logn)

    模板例如以下:

    int c[N], maxn;  
    inline int Lowbit(int x){return x&(-x);}  
    void change(int i, int x)//i点增量为x  
    {  
        while(i <= maxn)  
        {  
            c[i] += x;  
            i += Lowbit(i);  
        }  
    }  
    int sum(int x){//区间求和 [1,x]  
        int ans = 0;  
        for(int i = x; i >= 1; i -= Lowbit(i))  
            ans += c[i];  
        return ans;  
    }  

    怎样运用树状数组进行区间操作

    先定义两个树状数组 X, Y

    如今我们须要对一个数组 int a[N]; 进行区间操作:[L, R] += val 即 for i:L to R a[i] += val; 

    再定义一个 int size = R-L+1 , 即区间长度

    相应的改动是 

    1、X[L] += val;   X[R+1] -= val;

    2、Y[L] += -1 * val * (L-1);   Y[R+1] += val * R;

    相应的查询是

    当我们求和  时在树状数组中操作是 ans = X.sum(k) * k + Y.sum(k)

    分类讨论一下k分别在 [1,L-1] , [L, R] , [R+1, +]

    1、k[1,L-1]  

    显然 X.sum(k) == 0 且 Y.sum(k) == 0 -> ans = X.sum(k)*k + Y.sum(k) = 0*i+0 = 0 结果与实际相符。

    2、k[L, R] 

    X.sum(k) * k = X[L] * k = val * k,   Y.sum(k) = Y[L] =  -1 * val * (L-1) 

    ans = val * k - val * (L-1) = val * ( k - (L-1) ); 

    3、k[R+1, ]

    X.sum(k) * k = ( x[L] + x[R] ) * k = 0 * k = 0;

    Y.sum(k) = Y[L] + Y[R] = -val * (L-1) + val * R = val * (R-L+1) = val * size

    X.sum(k) * k + Y.sum(k) = val * size

    证明完成。

    下面模版中两个树状数组c[0], c[1] 相应上述的X, Y

    区间改动:add(L, R, val)

    求 int a[N]的前缀和 get_pre(R)

    区间查询:get(L,R)

    const int N = 4e5 + 100;
    template<class T>
    struct Tree{
    	T c[2][N];
    	int maxn;
    	void init(int x){
    		maxn = x+10; memset(c, 0, sizeof c);
    	}
    	inline int lowbit(int x){ return x&-x; }
    	T sum(T *b, int x){
    		T ans = 0;
    		if (x == 0)ans = b[0];
    		while (x)ans += b[x], x -= lowbit(x);
    		return ans;
    	}
    	void change(T *b, int x, T value){
    		if (x == 0)b[x] += value, x++;
    		while (x <= maxn)b[x] += value, x += lowbit(x);
    	}
    	T get_pre(int r){
    		return sum(c[0], r) * r + sum(c[1], r);
    	}
    	void add(int l, int r, T value){//区间加权
    		change(c[0], l, value);
    		change(c[0], r + 1, -value);
    		change(c[1], l, value * (-l + 1));
    		change(c[1], r + 1, value * r);
    	}
    	T get(int l, int r){//区间求和
    		return get_pre(r) - get_pre(l - 1);
    	}
    };
    Tree<ll> tree;

    好了,回归正题,我们来讲一下这道题的题意:

    题意:给定n*m的二维平面 w个操作

    int mp[n][m] = { 0 };

    1、0 (x1,y1) (x2,y2) value

    for i : x1 to x2

    for j : y1 to y2 

    mp[i][j] += value;

    2、1 (x1, y1) (x2 y2)

    ans1 = 纵坐标在 y1,y2间的总数

    ans2 = 横坐标不在x1,x2间的总数

    puts(ans1-ans2);

    代码例如以下:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    typedef long long ll;
    const int N = 4e6+10;
    int n, m, w;
    template<class T>  
    struct Tree
    {  
        ll c[2][N];  
        int maxn;  
        void init(int x)
    	{  
            maxn = x+10; memset(c, 0, sizeof c);  
        }  
        inline int lowbit(int x)
    	{ 
    		return x&-x; 
    	}  
        ll sum(ll *b, int x)
    	{  
            ll ans = 0;  
            if (x == 0)ans = b[0];  
            while (x)ans += b[x], x -= lowbit(x);  
            return ans;  
        }  
        void change(ll *b, int x, ll value)
    	{  
            if (x == 0)b[x] += value, x++;  
            while (x <= maxn)b[x] += value, x += lowbit(x);  
        }  
        ll get_pre(int r)
    	{  
            return sum(c[0], r) * r + sum(c[1], r);  
        }  
        void add(int l, int r, ll value)
    	{  
            change(c[0], l, value);  
            change(c[0], r + 1, -value);  
            change(c[1], l, value * (-l + 1));  
            change(c[1], r + 1, value * r);  
        }  
        ll get(int l, int r)
    	{  
            return get_pre(r) - get_pre(l - 1);  
        }  
    };
      
    Tree<ll> x, y;  
    
    int main()
    {
    	scanf("%d%d%d", &n, &m, &w);
    	 x.init(n); y.init(m); 
    	int tmp;
    	ll all = 0;
    	while(w--)
    	{
    		scanf("%d", &tmp);
    		int x1, x2, y1, y2, v;
    		if(tmp == 0)
    		{
    			scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &v);
    			all += v * (x2-x1+1) * (y2-y1+1);
    			x.add(x1, x2, v * (y2 - y1 + 1));
    			y.add(y1, y2, v * (x2 - x1 + 1));
    		}
    		if(tmp == 1)
    		{
    			scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
    			printf("%I64d
    ", y.get(1, y2) - y.get(1, y1-1) - (all - x.get(1, x2) + x.get(1, x1 - 1)));	
    		}
    	}
    	return 0;
    }
    







  • 相关阅读:
    MyBatis总结六:resultMap详解(包含多表查询)
    MyBatis总结五:#{}和${}的用法和区别
    MyBatis总结四:配置文件xml详解
    MyBatis使用动态代理报 invalid bound statement (not found) 错
    MyBatis总结三:使用动态代理实现dao接口
    MyBatis总结二:增删改查
    session详解&和cookie的区别
    cookie详解
    C#属性器Get和Set
    ORM实例介绍
  • 原文地址:https://www.cnblogs.com/mthoutai/p/6780958.html
Copyright © 2020-2023  润新知