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\n", y.get(1, y2) - y.get(1, y1-1) - (all - x.get(1, x2) + x.get(1, x1 - 1)));
		}
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-09-30 15:25:39

CodeForces 390E Inna and Large Sweet Matrix(树状数组改段求段)的相关文章

Codeforces 390E Inna and Large Sweet Matrix 树状数组改段求段

题目链接:点击打开链接 题意:给定n*m的二维平面 w个操作 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); 因为n最大是4e6, 所以用树状数组改段求段代替线段树 #include <stdio.h> #include &

树状数组改点求段

#include<stdio.h> int a[20],n; int lowbit(int x) { return x&(-x); } void add(int x,int c) { int i; for(i=x; i<=n; i+=lowbit(i))a[i]+=c; } int sum(int x) { int s=0,i; for(i=x; i; i-=lowbit(i))s+=a[i]; return s; } int main() { scanf("%d&qu

Codeforces 220B - Little Elephant and Array 离线树状数组

This problem can be solve in simpler O(NsqrtN) solution, but I will describe O(NlogN) one. We will solve this problem in offline. For each x (0?≤?x?<?n) we should keep all the queries that end in x. Iterate that x from 0 to n?-?1. Also we need to kee

Codeforces 459D Pashmak and Parmida&#39;s problem(树状数组)

题目链接:Codeforces 459D Pashmak and Parmida's problem 题目大意:给定一个序列,定义f(l,r,x)为l≤k≤r并且ak=x的k的个数,求1≤i<j≤n的情况下,f(1,i,ai)<f(j,n,aj)的个数. 解题思路:预处理出f(1,i,ai),f(j,n,aj)值,然后用树状数组维护个数. #include <cstdio> #include <cstring> #include <algorithm> us

CodeForces 380C Sereja and Brackets(扫描线+树状数组)

[题目链接] http://codeforces.com/problemset/problem/380/C [题目大意] 给出一个括号序列,求区间内左右括号匹配的个数. [题解] 我们发现对于每个右括号,其匹配的左括号是固定的, 我们保存每个右括号匹配的左括号位置, 对区间询问进行线扫描,将扫描的区间右端点及其之前所有的右括号对应的左括号位置做标记, 只要查询询问区间的标记个数就是答案,这个可以用树状数组维护. [代码] #include <cstdio> #include <algor

codeforces 703D D. Mishka and Interesting sum(树状数组)

题目链接: D. Mishka and Interesting sum time limit per test 3.5 seconds memory limit per test 256 megabytes input standard input output standard output Little Mishka enjoys programming. Since her birthday has just passed, her friends decided to present h

CF Educational Codeforces Round 10 D. Nested Segments 离散化+树状数组

题目链接:http://codeforces.com/problemset/problem/652/D 大意:给若干个线段,保证线段端点不重合,问每个线段内部包含了多少个线段. 方法是对所有线段的端点值离散化,按照左端点从大到小排序,顺着这个顺序处理所有线段,那么满足在它内部的线段一定是之前已经扫到过的.用树状数组判断有多少是在右端点范围内. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm>

codeforces round 512 F. Putting Boxes Together 树状数组维护区间加权平均数

F. Putting Boxes Together time limit per test 2.5 seconds memory limit per test 256 megabytes input standard input output standard output There is an infinite line consisting of cells. There are nn boxes in some cells of this line. The ii-th box stan

POJ 2155 Matrix (树状数组 &amp;&amp; 区间计数)

题意 : 给出一个N*N的矩阵, 矩阵只有可能包含0或1, 一开始则全部是0.对于矩阵可以进行两种操作, 第一种是输入 C x1 y1 x2 y2 表示, 对以(x1, y1)为左上角, 以(x2, y2)为右下角构成的矩形区域内的数全部进行取反操作, 即0变1.1变0.第二种是Q X Y, 表示查询现在这个矩阵的(X, Y)点到底是0还是1.总共有T次操作, 对于C操作进行相应的修改, 对于Q操作要对应输出! 分析 : 据说是楼教主出的题, 自己确实想不出什么高效的办法, 参考网上的题解, 才