codevs 1082 線段樹練習 3 樹狀數組 改段求段

題目

這裏是樹狀數組頻道。

哈!經過我深度挖掘各種blogs,終於看懂了!太辣雞了我!

題目大意

給你N個數,有兩種操作:

1:給區間[a,b]的所有數增加X

2:詢問區間[a,b]的數的和。

數據範圍:1<=n<=200000,1<=q<=200000

題目分析

一直覺得樹狀數組是個很神奇的東西。

如果你已經看過很多篇blogs覺得走投無路了,壓抑住(huaji)還是請認真看看ba!

雖然我非常非常蒟,但是我語文(我也需要表達對吧)數學還是過得去的(屁)...

如果你不會樹狀數組的改點求段和改段求點的話,對不起我幫不了你(踢下車/誠懇/)。

首先介紹差分數組c[],emm是個好東西

c[1]=a[1]−a[0]

c[2]=a[2]−a[1]

c[3]=a[3]−a[2]

...

c[n]=a[n]−a[n−1]

改段求點的核心也就是這個啦。

那麼接着來!!!(我加粗了!重要!)

    a[1]+a[2]+...+a[n]

=(c[1])+(c[1]+c[2])+...+(c[1]+c[2]+...+c[n])

=   n*c[1]+(n−1)*c[2]+...+c[n]

=   n*(c[1]+c[2]+...+c[n])−(0*c[1]+1*c[2]+...+(n-1)*c[n])

那麼我們就維護一個數組cc[n],其中cc[i]=(i−1)*c[i]。

上面的黑塊摘自:https://blog.csdn.net/weixin_41190227/article/details/82666855

一起來舉一反三ba

求區間[L,R]的值:

    a[L]+a[L+1]+....+a[R]

=(a[1]+a[2]+...+a[R])(a[1]+a[2]+...+a[L−1])

=((c[1])+(c[1]+c[2])+...+(c[1]+c[2]+...+c[R]))

    ((c[1])+(c[1]+c[2])+...+(c[1]+c[2]+...+c[L−1]))

=(R*c[1]+(R−1)*c[2]+...+1*c[R])((L−2)*c[1]+(L−3)*c[2]+...+1*c[L−1])

=(R*(c[1]+c[2]+...+c[R])−(0*c[1]+1*c[2]+...+(R−1)*c[R]))

    −((L−1)*(c[1]+c[2]+...+c[L−1])−(0*c[1]+1*c[2]+...+(L−2)*c[L−1]))

=(R*sum(c,R)−sum(cc,R))((L−1)*sum(c,L−1)−sum(c1,L−1))

你一定看懂了昂(大霧),恭喜你學會求區間。

然後改段跟改段求點裏面的是一模一樣的...

你修改c的時候,順手修改一下cc就好了。

	add(c,x,k);
	add(c,y+1,-k);//修改c 
	add(cc,x,(x-1)*k);
	add(cc,y+1,y*(-k));//修改cc 

 以上。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n,m; long long x,xx=0;
long long c[200010],cc[200010];
int lowbit(int x) { return x&-x; }

void add(long long a[],int x,long long k)
{
	while(x<=n)
	{
		a[x]+=k;
		x+=lowbit(x);
	}
}

long long sum(long long a[],int x)
{
	long long s=0;
	while(x)
	{
		s+=a[x];
		x-=lowbit(x);
	}
	return s;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		add(c,i,x-xx);
		add(cc,i,(i-1)*(x-xx));
		xx=x;
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		int t,x,y; long long k;
		scanf("%d%d%d",&t,&x,&y);
		if(t==1)
		{
			scanf("%lld",&k);
			add(c,x,k); add(c,y+1,-k);
			add(cc,x,(x-1)*k); add(cc,y+1,y*(-k));
		}
		else
		{
			long long aa=y*sum(c,y)-sum(cc,y);
			long long bb=(x-1)*sum(c,x-1)-sum(cc,x-1);
			printf("%lld\n",aa-bb);
		}
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章