树状数组与差分思想 (洛谷P3368)

今天一上午都在学习树状数组的思想及使用 感受很多

先看一下树状数组模版吧

int lowbit(int x)
{
	return x&(-x);
}
void update(int i,int val)
{
	while(i<=n)
	{
		c[i]+=val;
		i+=lowbit(i);
	}
}
int sum(int i)
{
	int ans=0;
	while(i>0)
	{
		ans+=c[i];
		i-=lowbit(i);
	}
	return ans;
}

主要就这三个函数

update函数用来进行单点更新操作 sum函数用来求和

我是看这篇博客弄懂的 我觉得写的特别好    掌握树状数组~彻底入门

差分数组的定义及用途

1.定义:

对于已知有n个元素的离线数列d,我们可以建立记录它每项与前一项差值的差分数组f:显然,f[1]=d[1]-0=d[1];对于整数i∈[2,n],我们让f[i]=d[i]-d[i-1]。

2.简单性质:

(1)计算数列各项的值:观察d[2]=f[1]+f[2]=d[1]+d[2]-d[1]=d[2]可知,数列第i项的值是可以用差分数组的前i项的和计算的,即d[i]=f[i]的前缀和。
(2)计算数列每一项的前缀和:第i项的前缀和即为数列前i项的和,那么推导可知

即可用差分数组求出数列前缀和;

3.用途:

(1)快速处理区间加减操作:

假如现在对数列中区间[L,R]上的数加上x,我们通过性质(1)知道,第一个受影响的差分数组中的元素为f[L],即令f[L]+=x,那么后面数列元素在计算过程中都会加上x;最后一个受影响的差分数组中的元素为f[R],所以令f[R+1]-=x,即可保证不会影响到R以后数列元素的计算。这样我们不必对区间内每一个数进行处理,只需处理两个差分后的数即可;

(2)询问区间和问题:

由性质(2)我们可以计算出数列各项的前缀和数组sum各项的值;那么显然,区间[L,R]的和即为ans=sum[R]-sum[L-1];

扫描二维码关注公众号,回复: 4510958 查看本文章

以洛谷P3368为例  题目链接

很简单的题意  我刚开始想的是能不能只用差分来写,发现不行,操作1很简单,但是操作2 求某个数的值,在差分数组里要求前缀和,这样就比较麻烦了,复杂度高

然后就想到用树状数组维护,操作2 求某个数的值可以直接用sum函数 log时间就够了

最后复杂度就是nlogn  代码如下

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string.h>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<vector>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;
const int maxn =5e5+5;
const int maxm=10000;
const int mod =1e9+7;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
int a[maxn];
int c[maxn];
int n,m;
inline int lowbit(int x)
{
    return x&(-x);
}
void update(int i,int val)
{
    while(i<=n)
    {
        c[i]+=val;
        i+=lowbit(i);
    }
}
int sum(int i)
{
    int ans=0;
    while(i>0)
    {
        ans+=c[i];
        i-=lowbit(i);
    }
    return ans;
}
int main()
{
    int ans=0;int vis=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        update(i,a[i]-vis);
        vis=a[i];
    }
    while(m--)
    {
        int q;scanf("%d",&q);
        if(q==1)
        {
            int x,y,k;scanf("%d%d%d",&x,&y,&k);
            update(x,k);
            update(y+1,-k);		
        }
        if(q==2)
        {
        	int x;scanf("%d",&x);
        	printf("%d\n",sum(x));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wzazzy/article/details/84972509