最近花近两个星期敲线(da)段(you)树(xi)。其实花了一节课磕磕巴巴就打出来了然而总是不过,然后开始漫长的找错征途。。。
此处省略一万行泪;
还好有题解和磊磊不然一年都找不出来错/捂脸
codevs4919
线段树练习4
给你N个数,有两种操作
1:给区间[a,b]内的所有数都增加X
2:询问区间[a,b]能被7整除的个数
区间修改区间查询
结构体里添加sev [ 7 ]数组
sev [ k ] 表示当前区间模7余k的数的个数
然后是标记
一开始想到的是add时
如果add范围全覆盖到当前区间
当前区间修改
加到lazy
然后回去时沿路更新父节点值
query的时候遇到有lazy的区间就下传lazy
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define blank " "
using namespace std;
ll read()
{
ll ans=0;
bool b=1;
char c=getchar();
while(!isdigit(c))
{
if(c=='-')b=0;
c=getchar();
}
while(isdigit(c))
{
ans=ans*10+(c-'0');
c=getchar();
}
if(b)return ans;
else return -ans;
}
int a[1000010] , sevv[20];
struct tree{
tree* lc,* rc;
int l,r;
int sev[7];
int lazy;
/*tree() {
lazy=0;
for(int i=0;i<=6;i++)
{
sev[i]=0;
}
}*/
}*root;
void pushup(tree* node)
{
for(int i=0;i<=6;i++)
{
node->sev[i]=0;
}
for(int i=0;i<=6;i++)
{
node->sev[i]=node->lc->sev[i]+node->rc->sev[i];
}
}
void pushdown(tree* node)
{
if(node->lazy)
{
node->lc->lazy+=node->lazy;
node->lc->lazy%=7;
node->rc->lazy+=node->lazy;
node->rc->lazy%=7;
int s=node->lazy;
for(int i=0;i<=6;i++)
{
sevv[i]=0;
}
for(int i=0;i<=6;i++)
{
sevv[(i+s)%7]=node->lc->sev[i];
sevv[(i+s)%7+7]=node->rc->sev[i];
}
for(int i=0;i<=6;i++)
{
node->lc->sev[i]=sevv[i];
node->rc->sev[i]=sevv[i+7];
}
node->lazy=0;
}
}
tree* build(int l,int r)
{
tree* node=new tree;
if(l==r)
{
for(int i=0;i<=6;i++)
{
node->sev[i]=0;
}
node->sev[a[l]]=1;
}
else
{
int mid=(l+r)>>1;
node->lc=build(l,mid);
node->rc=build(mid+1,r);
pushup(node);
}
node->l=l;node->r=r;
node->lazy=0;
return node;
}
void add(tree* node,int l,int r,int s)
{
if(node->l==l&&node->r==r)
{
node->lazy+=s;
for(int i=0;i<=6;i++)
{
sevv[i]=0;
}
for(int i=0;i<=6;i++)
{
sevv[(i+s)%7]=node->sev[i];
}
for(int i=0;i<=6;i++)
{
node->sev[i]=sevv[i];
}
return;
}
int mid=(node->l+node->r)>>1;
if(r<=mid){
add(node->lc,l,r,s);
}
else if(l>mid)
{
add(node->rc,l,r,s);
}
else {
add(node->lc,l,mid,s);
add(node->rc,mid+1,r,s);
}
pushup(node);
return;
}
int query(tree* node,int l,int r)
{
if(node->l==l&&node->r==r)
{
return node->sev[0];
}
else {
pushdown(node);
int mid=(node->l+node->r)>>1;
if(r<=mid)
{
return query(node->lc,l,r);
}
else if(l>mid)
{
return query(node->rc,l,r);
}
else {
return query(node->lc,l,mid)+query(node->rc,mid+1,r);
}
}
}
int main()
{
//ios::sync_with_stdio(false);
int n=read();
for(int i=1;i<=n;i++)
{
a[i]=read()%7;
}
root=build(1,n);
int m=read();
for(int i=1;i<=m;i++)
{
string s;
cin>>s;
if(s=="add")
{
int aa=read(),bb=read(),cc=read()%7;
add(root,aa,bb,cc);
}
else
{
int aa=read(),bb=read();
int ans=query(root,aa,bb);
cout<"\n";
}
}
return 0;
}
以上是第一个版本
不出所料 听取wa声一片
然后听磊磊说
add的时候
如果碰到lazy标记的话
也要下传哟
我表示不服
于是乎
磊磊拿笔在纸上画了画
果然磊磊专治各种不服
我服了
第一个重点来了
为什么add的时候也要下传lazy
因为add写的是先修改目标区间然后回来时更新
如果不下传lazy的话
可以想一下
当前访问到了这个区间
但是父节点的区间有lazy
有lazy表示应该有修改但是儿子节点(就是当前这个区间)还没有修改
但是此时目的区间就是当前的区间
于是add修改了当前区间(还未加上父节点的lazy)的值,然后当前节点打上lazy 返回
注意他返回的时候是要顺带修改父节点的
要顺带修改父节点的!!
然后就直接把本来已经修改好的父节点
改成错的了。。
然后改了改
void add(tree* node,int l,int r,int s)
{
pushdown(node);
if(node->l==l&&node->r==r)
{
node->lazy+=s;
for(int i=0;i<=6;i++)
{
sevv[i]=0;
}
for(int i=0;i<=6;i++)
{
sevv[(i+s)%7]=node->sev[i];
}
for(int i=0;i<=6;i++)
{
node->sev[i]=sevv[i];
}
return;
}
int mid=(node->l+node->r)>>1;
if(r<=mid){
add(node->lc,l,r,s);
}
else if(l>mid)
{
add(node->rc,l,r,s);
}
else {
add(node->lc,l,mid,s);
add(node->rc,mid+1,r,s);
}
pushup(node);
return;
}
加个pushdown不就好了
然后
RE
。
后来找错实在找不出来对了下题解
题解大概是这么写的
void add(tree* node,int l,int r,int s)
{
if(node->l==l&&node->r==r)
{
node->lazy+=s;
for(int i=0;i<=6;i++)
{
sevv[i]=0;
}
for(int i=0;i<=6;i++)
{
sevv[(i+s)%7]=node->sev[i];
}
for(int i=0;i<=6;i++)
{
node->sev[i]=sevv[i];
}
return;
}
pushdown(node);
int mid=(node->l+node->r)>>1;
if(r<=mid){
add(node->lc,l,r,s);
}
else if(l>mid)
{
add(node->rc,l,r,s);
}
else {
add(node->lc,l,mid,s);
add(node->rc,mid+1,r,s);
}
pushup(node);
return;
}
pushdown的位置在下面
恍然大悟
我那个版本到最后一个节点再pushdown的话肯定会访问无效内存的啊!
嗨吖气死
那我改成这样不行么
void add(tree* node,int l,int r,int s)
{
if(l!=r)pushdown(node);
if(node->l==l&&node->r==r)
{
node->lazy+=s;
for(int i=0;i<=6;i++)
{
sevv[i]=0;
}
for(int i=0;i<=6;i++)
{
sevv[(i+s)%7]=node->sev[i];
}
for(int i=0;i<=6;i++)
{
node->sev[i]=sevv[i];
}
return;
}
int mid=(node->l+node->r)>>1;
if(r<=mid){
add(node->lc,l,r,s);
}
else if(l>mid)
{
add(node->rc,l,r,s);
}
else {
add(node->lc,l,mid,s);
add(node->rc,mid+1,r,s);
}
pushup(node);
return;
}
还真不行
依然是WA
为啥呢
因为又把node->l node->r 和l r 弄混了
前者是区间左右端点
后者是查询的区间端点
!!
以后可别弄混了 这是真难查
线段树好麻烦
THE END