树状数组+ 成段更新+ 成段输出 裸题 Fzu 2171 防守阵地2

本渣战5,感谢start的博客,强烈推荐,树状数组3种不同的写法点击打开链接

(想学树状数组可以去看下神犇的这篇博客,很有帮助!本文写给看完那篇博客但还不是很理解成段更新的acmer)

此文只是阐述一下个人对树状数组+ 成段更新的个人理解。

一、 想学好树状数组,就一定要对这幅图很熟悉!


笔者指的熟悉是要至少不会对x+=x&(-x),x-=x&(-x),这两种 x  向 n 遍历和 x 向 0遍历的方式感到 疑惑(模糊)。

一定要明白  Cx   表示的是 1 -  x  的 和阿 =  = ,而且例如C7 = A[ 7 ] +C6 + C4.具体跟二进制有关(感觉会讲不清不讲了,读者自己体会下,大神请无视.......Orz)


二、

   莫队算法,= =学过的应该比较好理解一些。

个人感觉成段更新 这个代码和莫队算法的思想很像,都是标记了 ( L , R)   不同的是两个辅助数组B  ,C 。B :R -> 0 + C,L -> 0 - C: R - > N,+ C*R ;L->N  -C*L;

//操作:
ADD_B(r, c); ADD_C(r, c);
if (l > 1) {ADD_B(l - 1, -c); ADD_C(l - 1, -c);}

至于   成段输出,可以输入时 另设一个A数组A[ i ] + = A[ i - 1];A[I]表示1-> I的和,至于区间 ( L , R) 的和原始的可以表示为A[ R ] - A[ L - 1 ];

然后再加上你对这个区间更新过后的值的和就OK了。

//A[m-1+x]-A[m-1]+ Sum(m-1+x)-Sum(m-1),这个就是题目要求的值

int sum_B(int x)
{
	int sum = 0;
	for(int i= x;i <= n; i+=i&(-i))sum+=B[i];
	return sum;
}

int sum_C(int x )
{
	int sum = 0;
	for(int i= x;i > 0; i-=i&(-i))sum+=C[i];
	return sum;
}

int Sum(int x)
{
	if(x)return sum_B(x) * x + sum_C(x-1);
	else return 0;
}


下面附上完整AC代码,281 ms 1364KB 1072B:

#include<stdio.h>
#include<string.h> 
//快12点了注释就不打了。。= = 
int A[100005];//A[I]表示1-I的士兵能力和 
int B[100005];
int C[100005];
int n;

void add_B(int x,int c)
{
	for(int i = x;i > 0; i-=i&(-i))B[i]+=c;
}
void add_C(int x,int c)
{
	for(int i= x;i <= n; i+=i&(-i))C[i]+=x*c;
}

int sum_B(int x)
{
	int sum = 0;
	for(int i= x;i <= n; i+=i&(-i))sum+=B[i];
	return sum;
}

int sum_C(int x )
{
	int sum = 0;
	for(int i= x;i > 0; i-=i&(-i))sum+=C[i];
	return sum;
}

int Sum(int x)
{
	if(x)return sum_B(x) * x + sum_C(x-1);
	else return 0;
}



int main()
{
	int x, k;
	
	while(~scanf("%d%d%d",&n,&x,&k))
	{
		memset(C,0,sizeof(C));
		memset(B,0,sizeof(B));
		for(int i = 1;i <= n; i++)
		{
			scanf("%d",&A[i]);
           if(i>1)A[i]+=A[i-1]; 
		}
	
		int m;	
		while(k--)
		{
			scanf("%d",&m);
			printf("%d\n",A[m-1+x]-A[m-1]+ Sum(m-1+x)-Sum(m-1));
			
			add_B(m-1+x,-1),add_C(m-1+x,-1);
			
			if(m-1)add_B(m-1,1),add_C(m-1,1);
			
			
		}
		
		
	}
	
	return 0;
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值