题解(线段树区间修改模板及lazytag) 寒假3 C.简单整数问题(多错题)

zhouwc

2018-02-25 09:39:13

Personal

问题 C: 简单整数问题 时间限制: 2 Sec 内存限制: 128 MB [提交][状态][讨论版] 题目描述 罗老师给大家N个数字,用A1, A2, …, AN表示,然后有两个操作: 1. C a b c: 表示给区间[ Aa, Ab ], 每个数上增加c的值 2. Q a b:表示询问区间[ Aa, Ab ]的总和 输入 输入N Q表示N个数和Q个操作 然后输入一行 N个数,表示每个数字初始值 然后输入Q行,每行一个操作,如题目描述的两种操作 输出 对于每个询问操作,输出区间和 样例输入 10 5 1 2 3 4 5 6 7 8 9 10 Q 4 4 Q 1 10 Q 2 4 C 3 6 3 Q 2 4 样例输出 4 55 9 15 提示 【数据规模和约定】 1<=N,Q<=100000 -100000000<=Ai<=100000000 -10000<=c<=10000 题解 题解1(罗老师本来在题目中写着的) tr[i]表示线段树上的结点,表示[st, ed]的未更新前的区间和 ad[i]表示线段树上的[st, ed]更新了多少值 难点在于,更新的值需要传递下去,回更新上来 ```cpp void pushDown(int p) { ad[p+p]+=ad[p]; ad[p+p+1]+=ad[p]; ad[p]=0LL; } void pullUp(int p,int len) { tr[p]=tr[p<<1]+ad[p<<1]*(len-(len>>1))+tr[p<<1|1]+ad[p<<1|1]*(len>>1); } ``` 题解2(我自己写的,并且经过无数次错误之后) 哎,就差那么一点点就对了。。。这本是一道模板题。。。 结果找了一个下午+一个晚上。改了无数个地方,最后还是请罗老师帮忙。 细节决定成败啊。。。 错误原因:1、addv,tree,dfs(int)必须都要开成longlong,我忽略了addv。 2、在dfs中,原本A,B两题直接可以return,而这道题因为要修改区间。有这个懒惰标记(lazytag)的影响,需要在查找值的时候,顺带改变当前初始的一些值。即我们会因为addv【p】的下移,导致addv【p】=0。且tree【p】的值也会因为下面两个儿子的值的改变而改变,所以需要对tree【p】的值进行重新计算(在dfs,update中都需要),而在dfs中,我直接加上了重新计算这个语句。但是却忽略了在前面的查找(二分)中,直接把dfs的只给return了。这直接造成了后面这个重新计算语句的失效,而addv的值已经被剪掉了,这是问题之所在。 最后,我想说,细节决定成败!!! 本来的错误代码:错误!!! ```cpp #include<stdio.h> using namespace std; long long tree[400005]; int a[100005],n,m,x,y,addv[400005]; char b[3]; /* void change(int p,int l,int r,int q,int v)//单点修改部分 { if (l==r) { tree[p]=v; return; } int mid=(l+r)/2; if (q<=mid) change(p*2,l,mid,q,v); else change(p*2+1,mid+1,r,q,v); tree[p]=min(tree[p*2],tree[p*2+1]); } */ void update(int l,int r,int p,int ql,int qr,int c) { if (l==ql&&qr==r) { addv[p]=addv[p]+c; return; } addv[p*2]+=addv[p]; addv[p*2+1]+=addv[p]; addv[p]=0; int mid=(l+r)/2; /* if (l<=mid) update(l,mid,p*2,ql,qr,c); if (r>mid) update(mid+1,r,p*2+1,ql,qr,c); tree[p]=tree[p*2]+tree[p*2+1]+addv[p*2]*(mid-l)+addv[p*2+1]*(r-mid); */ if (qr<=mid) { update(l,mid,p*2,ql,qr,c); }else if (ql>mid) {update(mid+1,r,p*2+1,ql,qr,c);}else { update(l,mid,p*2,ql,mid,c); update(mid+1,r,p*2+1,mid+1,qr,c); } tree[p]=tree[p*2]+tree[p*2+1]+addv[p*2]*(mid-l+1)+addv[p*2+1]*(r-mid); } long long dfs(int l,int r,int p,int s,int t) { if (l==s&&r==t) return tree[p]+addv[p]*(r-l+1); else { int mid=(l+r)/2; addv[p*2]+=addv[p]; addv[p*2+1]+=addv[p]; addv[p]=0; if (t<=mid) return dfs(l,mid,p*2,s,t); else if (s>mid) return dfs(mid+1,r,p*2+1,s,t); else { return dfs(l,mid,p*2,s,mid)+dfs(mid+1,r,p*2+1,mid+1,t); } tree[p]=tree[p*2]+tree[p*2+1]+addv[p*2]*(mid-l+1)+addv[p*2+1]*(r-mid); } } void build(int l,int r,int p) { if (l==r) { tree[p]=a[l]; return; } else { int mid=(l+r)/2; build(l,mid,p*2); build(mid+1,r,p*2+1); tree[p]=tree[p*2]+tree[p*2+1]; } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,n,1); int k=0; for (int i=1;i<=m;i++) { scanf("%s",b); if (b[0]=='Q') { scanf("%d%d",&x,&y); printf("%lld\n",dfs(1,n,1,x,y)); k++; } else { int z=0; scanf("%d%d%d",&x,&y,&z); update(1,n,1,x,y,z); } } } ``` 经过修改后的AC代码,在//即一开始错误之处。 ```cpp #include<stdio.h> using namespace std; long long tree[400005]; int a[100005],n,m,x,y; long long addv[400005]; //addv也改成long long 类型 char b[3]; /* void change(int p,int l,int r,int q,int v)//单点修改部分 { if (l==r) { tree[p]=v; return; } int mid=(l+r)/2; if (q<=mid) change(p*2,l,mid,q,v); else change(p*2+1,mid+1,r,q,v); tree[p]=min(tree[p*2],tree[p*2+1]); } */ void update(int l,int r,int p,int ql,int qr,int c) { if (l==ql&&qr==r) { addv[p]=addv[p]+c; return; } addv[p*2]+=addv[p]; addv[p*2+1]+=addv[p]; addv[p]=0; int mid=(l+r)/2; /* if (l<=mid) update(l,mid,p*2,ql,qr,c); if (r>mid) update(mid+1,r,p*2+1,ql,qr,c); tree[p]=tree[p*2]+tree[p*2+1]+addv[p*2]*(mid-l)+addv[p*2+1]*(r-mid); */ if (qr<=mid) { update(l,mid,p*2,ql,qr,c); }else if (ql>mid) {update(mid+1,r,p*2+1,ql,qr,c);}else { update(l,mid,p*2,ql,mid,c); update(mid+1,r,p*2+1,mid+1,qr,c); } tree[p]=tree[p*2]+tree[p*2+1]+addv[p*2]*(mid-l+1)+addv[p*2+1]*(r-mid); } long long dfs(int l,int r,int p,int s,int t) { if (l==s&&r==t) return tree[p]+addv[p]*(r-l+1); else { int mid=(l+r)/2; addv[p*2]+=addv[p]; addv[p*2+1]+=addv[p]; addv[p]=0; long long ret=0; //设置一个返回值变量 if (t<=mid) ret=dfs(l,mid,p*2,s,t); else if (s>mid) ret=dfs(mid+1,r,p*2+1,s,t); else{ ret=dfs(l,mid,p*2,s,mid)+dfs(mid+1,r,p*2+1,mid+1,t); //这里不能return } tree[p]=tree[p*2]+tree[p*2+1]+addv[p*2]*(mid-l+1)+addv[p*2+1]*(r-mid); return ret; } } void build(int l,int r,int p) { if (l==r) { tree[p]=a[l]; return; } else { int mid=(l+r)/2; build(l,mid,p*2); build(mid+1,r,p*2+1); tree[p]=tree[p*2]+tree[p*2+1]; } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,n,1); int k=0; for (int i=1;i<=m;i++) { scanf("%s",b); if (b[0]=='Q') { scanf("%d%d",&x,&y); printf("%lld\n",dfs(1,n,1,x,y)); k++; } else { int z=0; scanf("%d%d%d",&x,&y,&z); update(1,n,1,x,y,z); } } } ```