洛谷 P3372 【模板】线段树 1 题解
题面
题目链接:戳这里
题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和
输入输出格式
输入格式:
第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k
操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和
输出格式:
输出包含若干行整数,即为所有操作2的结果。
输入输出样例
输入样例#1:
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出样例#1:
11
8
20
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=8,M<=10
对于70%的数据:N<=1000,M<=10000
对于100%的数据:N<=100000,M<=100000
(数据已经过加强_,保证在int64/long long数据范围内)
样例说明:
题解
区间修改,区间查询。
直接贴代码吧(暂时没空写思路,大体就是懒标记,网上不少教程)
错误历程(感兴趣的可以试着查查错, 不感兴趣大佬们直接看最后的正解即可)
第一次(10分代码)
#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=100005;
struct tree{
ll l,r,v,laz;
}tr[maxn*4];
ll a[maxn];
#define len(x) (tr[x].r-tr[x].l+1)
ll buildtree(ll id,ll l,ll r){
tr[id].l=l,tr[id].r=r;tr[id].laz=0;
if(l==r){tr[id].v=a[l];return a[l];}
ll mid=l+r>>1;
tr[id].v=buildtree(id<<1,l,mid);
tr[id].v+=buildtree(id<<1|1,mid+1,r);
return tr[id].v;
}
void treeadd(ll al,ll ar,ll val,ll id){
ll l=tr[id].l,r=tr[id].r;
if(al>r||ar<l)return;
if(al<=l&&ar>=r){
tr[id].laz+=val;
tr[id].v+=val*len(id);
return;
}
treeadd(al,ar,val,id<<1);
treeadd(al,ar,val,id<<1|1);
tr[id].v=tr[id<<1].v+tr[id<<1|1].v;
return ;
}
ll query(ll al,ll ar,ll id){
ll l=tr[id].l,r=tr[id].r;
if(al>r||ar<l)return 0;
if(al<=l&&ar>=r)return tr[id].v;
tr[id<<1].laz+=tr[id].laz;tr[id<<1|1].laz+=tr[id].laz;
tr[id<<1].v+=tr[id].laz*len(id<<1);
tr[id<<1|1].v+=(tr[id].laz*len(id<<1|1));
tr[id].laz=0;
ll ans=query(al,ar,id<<1);
ans+=query(al,ar,id<<1|1);
return ans;
}
int main(){
ll n,m;scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
buildtree(1,1,n);
for(ll i=0;i<m;i++){
ll x,y,k,bb;scanf("%lld",&bb);
switch(bb){
case 1:{
scanf("%lld%lld%lld",&x,&y,&k);
treeadd(x,y,k,1);
break;
}
case 2:{
scanf("%lld%lld",&x,&y);
printf("%d\n",query(x,y,1));
break;
}
}
}
return 0;
}
第二次(70分代码)
#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=100005;
struct tree{
ll l,r,v,laz;
}tr[maxn*4];
ll a[maxn];
#define len(x) (tr[x].r-tr[x].l+1)
ll buildtree(ll id,ll l,ll r){
tr[id].l=l,tr[id].r=r;tr[id].laz=0;
if(l==r){tr[id].v=a[l];return a[l];}
ll mid=l+r>>1;
tr[id].v=buildtree(id<<1,l,mid);
tr[id].v+=buildtree(id<<1|1,mid+1,r);
return tr[id].v;
}
void treeadd(ll al,ll ar,ll val,ll id){
ll l=tr[id].l,r=tr[id].r;
if(al>r||ar<l)return;
if(al<=l&&ar>=r){
tr[id].laz+=val;
tr[id].v+=val*len(id);
return;
}
tr[id<<1].laz+=tr[id].laz;tr[id<<1|1].laz+=tr[id].laz;
tr[id<<1].v+=tr[id].laz*len(id<<1);
tr[id<<1|1].v+=tr[id].laz*len(id<<1|1);
tr[id].laz=0;
treeadd(al,ar,val,id<<1);
treeadd(al,ar,val,id<<1|1);
tr[id].v=tr[id<<1].v+tr[id<<1|1].v;
return ;
}
ll query(ll al,ll ar,ll id){
ll l=tr[id].l,r=tr[id].r;
if(al>r||ar<l)return 0;
if(al<=l&&ar>=r)return tr[id].v;
tr[id<<1].laz+=tr[id].laz;tr[id<<1|1].laz+=tr[id].laz;
tr[id<<1].v+=tr[id].laz*len(id<<1);
tr[id<<1|1].v+=tr[id].laz*len(id<<1|1);
tr[id].laz=0;
ll ans=query(al,ar,id<<1);
ans+=query(al,ar,id<<1|1);
return ans;
}
int main(){
ll n,m;scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
buildtree(1,1,n);
for(ll i=0;i<m;i++){
ll x,y,k,bb;scanf("%lld",&bb);
switch(bb){
case 1:{
scanf("%lld%lld%lld",&x,&y,&k);
treeadd(x,y,k,1);
break;
}
case 2:{
scanf("%lld%lld",&x,&y);
printf("%d\n",query(x,y,1));
break;
}
}
}
return 0;
}
正解:
#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=100005;
struct tree{
ll l,r,v,laz;
}tr[maxn*4];
ll a[maxn];
#define len(x) (tr[x].r-tr[x].l+1)
ll buildtree(ll id,ll l,ll r){
tr[id].l=l,tr[id].r=r;tr[id].laz=0;
if(l==r){tr[id].v=a[l];return a[l];}
ll mid=l+r>>1;
tr[id].v=buildtree(id<<1,l,mid);
tr[id].v+=buildtree(id<<1|1,mid+1,r);
return tr[id].v;
}
void treeadd(ll al,ll ar,ll val,ll id){
ll l=tr[id].l,r=tr[id].r;
if(al>r||ar<l)return;
if(al<=l&&ar>=r){
tr[id].laz+=val;
tr[id].v+=val*len(id);
return;
}
//第一次错误是因为区间加时忘记释放懒标记
tr[id<<1].laz+=tr[id].laz;tr[id<<1|1].laz+=tr[id].laz;
tr[id<<1].v+=tr[id].laz*len(id<<1);
tr[id<<1|1].v+=tr[id].laz*len(id<<1|1);
tr[id].laz=0;
treeadd(al,ar,val,id<<1);
treeadd(al,ar,val,id<<1|1);
tr[id].v=tr[id<<1].v+tr[id<<1|1].v;
return ;
}
ll query(ll al,ll ar,ll id){
ll l=tr[id].l,r=tr[id].r;
if(al>r||ar<l)return 0;
if(al<=l&&ar>=r)return tr[id].v;
tr[id<<1].laz+=tr[id].laz;tr[id<<1|1].laz+=tr[id].laz;
tr[id<<1].v+=tr[id].laz*len(id<<1);
tr[id<<1|1].v+=tr[id].laz*len(id<<1|1);
tr[id].laz=0;
ll ans=query(al,ar,id<<1);
ans+=query(al,ar,id<<1|1);
return ans;
}
int main(){
ll n,m;scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
buildtree(1,1,n);
for(ll i=0;i<m;i++){
ll x,y,k,bb;scanf("%lld",&bb);
switch(bb){
case 1:{
scanf("%lld%lld%lld",&x,&y,&k);
treeadd(x,y,k,1);
break;
}
case 2:{
scanf("%lld%lld",&x,&y);
//第二次错误是因为答案未用%lld输出
printf("%lld\n",query(x,y,1));
break;
}
}
}
return 0;
}
另注,define后面的字符串部分最好加上括号,我一开始 样例都没过 就是因为忘记加括号了然后乘法优先级高于加法,所以炸了