题目描述
在一个荒凉的墓地上
有一个令人尊敬的守墓人, 他看守的墓地从来
没有被盗过, 所以人们很放心的把自己的先人的墓
安顿在他那
守墓人能看好这片墓地是必然而不是偶然.....
因为....守墓人懂风水 0.0
他把墓地分为主要墓碑和次要墓碑, 主要墓碑
只能有 1 个, 守墓人把他记为 1 号, 而次要墓碑有
n-1 个,守墓人将之编号为 2,3...n,所以构成了一个有 n 个墓碑的墓地。
而每个墓碑有一个初始的风水值,这些风水值决定了墓地的风水的好坏,所以守墓人
需要经常来查询这些墓碑。
善于运用风水的守墓人,通过一次次逆天改命,使得自己拥有了无限寿命,没人知道
他活了多久。
这天,你幸运的拜访到了他,他要求你和他共同见证接下来几年他的战果,但不过他
每次统计风水值之和都需要你来帮他计算,算错了他会要你命 QAQ
风水也不是不可变,除非遭遇特殊情况,已知在接下来的 2147483647 年里,会有 n 次
灾难,守墓人会有几个操作:
1.将[l,r]这个区间所有的墓碑的风水值增加 k。
2.将主墓碑的风水值增加 k
3.将主墓碑的风水值减少 k
4.统计[l,r]这个区间所有的墓碑的风水值之和
5.求主墓碑的风水值
上面也说了,很多人会把先人的墓安居在这里,而且守墓人活了很多世纪→_→,墓碑
的数量会多的你不敢相信= =
守墓人和善的邀请你帮他完成这些操作,要不然哪天你的旅馆爆炸了,天上下刀子.....
为了活命,还是帮他吧
输入输出格式
输入格式:
第一行,两个正整数 n,f 表示共有 n 块墓碑,并且在接下来的
2147483647 年里,会有 f 次世界末日
第二行,n 个正整数,表示第 i 块墓碑的风水值
接下来 f 行,每行都会有一个针对世界末日的解决方案,如题所述,标记同题
输出格式:
输出会有若干行,对 4 和 5 的提问做出回答
输入输出样例
输入样例#1:
5 7 0 0 0 0 0 1 1 5 1 1 1 3 3 2 3 3 1 4 1 5 2 1 5
输出样例#1:
16 7
说明
20%的数据满足:1≤n≤100
50%的数据满足:1≤n≤6000
100%的数据满足:1≤n,f≤2*10^5
思路:这不是裸的线段树吗?但是我不会啊........那就用树状数组来搞吧。我的上篇博客用树状数组实现了线段树的区间修改区间查询功能,那就直接拿到这道题上用用。该题需要实现单点查询、区间查询、区间修改、单点修改功能,但是我用树状数组只实现了区间与单点查询。还差一个单点修改功能,另开一个数组实现吗?注意基础功能实现是建立在树状数组单点修改区间查询的基础上的,所以tree数组中存储的是差分值,所以把单点以区间的方式处理,注意需要修改数组p中tree[i]*(i-1)的前缀和。
加输入挂用时252ms,还行。
树状数组代码如下:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define INF 0x3f3f3f3f
#define ll long long
#define per(i,a,b) for(long long int i=a;i<=b;++i)
ll int n,m,tree[2000010],p[2000010];
ll int lowbit(ll int k)
{
return k&(-k);
}
void add(ll int x,ll int k,int fag)
{
while(x<=n)
{
if(fag==1) tree[x]+=k;
else p[x]+=k;
x+=lowbit(x);
}
}
ll int sum(ll int x,int fag)
{
ll int s=0;
while(x!=0)
{
if(fag==1) s+=tree[x];
else s+=p[x];
x-=lowbit(x);
}
return s;
}
int main()
{
scanf("%lld%lld",&n,&m);
ll int x,y=0;
per(i,1,n)
{
//ll int x,y;
scanf("%lld",&x);
y=x-y;
add(i,y,1);//区间查询单点修改,前缀和为该位置的值
add(i,(i-1)*y,2);//前缀和差值部分
y=x;
}
per(i,1,m)
{
ll int a,b,c,d;
scanf("%lld",&a);
if(a==1)
{
scanf("%lld%lld%lld",&b,&c,&d);
add(b,d,1);add(c+1,-d,1);//正常的插入
add(b,d*(b-1),2);add(c+1,-d*c,2);//正常的维护
}
if(a==2)
{
scanf("%lld",&b);
add(1,b,1);add(2,-b,1);
add(2,-b,2);
}
if(a==3)
{
scanf("%lld",&b);
add(1,-b,1);add(2,b,1);
add(2,b,2);
}
if(a==4)
{
scanf("%lld%lld",&b,&c);
printf("%lld\n",(c*sum(c,1)-(b-1)*sum(b-1,1))-(sum(c,2)-sum(b-1,2)));
}
if(a==5) printf("%lld\n",sum(1,1));
}
return 0;
}
线段树代码如下: