正常的线段树是这样的
[1,10]->[1,5]+[6,10];
我们统计区间和的时候也很方便每个区间的r-l+1就是区间长度
现在的问题是如果数据范围太大,维护一段区间的和就不简单了
例如以下对应关系
1 2 3 4 5 6
10 20 30 40 50 60
当区间[10,20]修改为1,线段树操作时为[1,2]修改为1,
当区间[20,30]修改为1,线段树操作时为[2,3]修改为1,
当我们循问区间[10,30]时,线段树操作时为询问[1,3]
如果还是按照之前的想法,[1,3]=[1,2]+[3,3]=(20-10+1)+(1-1+1)=12显然是错误的
我们需要重新定义线段
[a,b)=[a,mid)+[mid,b)
修改[10,20]是我们实际修改[10,21),修改[20,30]是我们实际修改[20,31)
询问[10,30],实际询问[10,31)
重新建立对应关系
1 2 3 4
10 20 21 31
修改[10,20],将[1,3)修改为1
修改[20,30],将[1,3)修改为1
询问[10,30],就是询问[1,4)
[1,4)=[1,2)+[2,4)
而[1,3)修改为1,[1,3)修改为1,使得[1,2)和[2,4)都为1
[1,4)=[1,2)+[2,4)=(20-10)+(31-20)=21
而[10,30]=(30-10+1)=21
实现方法:
将所有用到的区间[a,b],化成[a,b+1),将a和b+1两个数用于离散
区间和变为[x,y)=f[y]-f[x],函数f表示离散后的映射关系。
其余操作不变
区间赋值(赋值为1)的操作
void pushdown(int x,int l,int r,node *t)
{
if (t[x].d)
{
int mid=l+r>>1;
t[x<<1].d=1; t[x<<1].s=f[mid]-f[l];
t[x<<1|1].d=1; t[x<<1|1].s=f[r]-f[mid];
t[x].d=0;
}
}
void updata(int x,int l,int r,int fl,int fr,node *t)
{
if (l==fl && r==fr)
{
t[x].d=1;
t[x].s=f[r]-f[l];
return;
}
pushdown(x,l,r,t);
int mid=l+r>>1;
if (fr<=mid) updata(x<<1,l,mid,fl,fr,t);else
if (fl>=mid) updata(x<<1|1,mid,r,fl,fr,t);else
{
updata(x<<1,l,mid,fl,mid,t);
updata(x<<1|1,mid,r,mid,fr,t);
}
t[x].s=t[x<<1].s+t[x<<1|1].s;
}
询问操作
int query(int x,int l,int r,int fl,int fr,node *t)
{
if (l==fl && r==fr)return t[x].s;
pushdown(x,l,r,t);
int mid=l+r>>1;
if (fr<=mid) return query(x<<1,l,mid,fl,fr,t);else
if (fl>=mid) return query(x<<1|1,mid,r,fl,fr,t);else
return query(x<<1,l,mid,fl,mid,t)+query(x<<1|1,mid,r,mid,fr,t);
}
离散化操作
for (int i=1;i<=q;i++)
{
scc(,a[i],b[i]);
f[++cnt]=a[i];
f[++cnt]=b[i]+1;
}
f[++cnt]=n+1;
sort(f+1,f+cnt+1);
cnt=unique(f+1,f+cnt+1)-f-1;
int nn=lb(f+1,f+cnt+1,n+1)-f;
使用方法
for (int i=1;i<=q;i++)
{
int x=lower_bound(f+1,f+cnt+1,a[i])-f,y=lower_bound(f+1,f+cnt+1,b[i]+1)-f;
updata(1,1,nn,x,y,t);
query(1,1,nn,x,y,t);
}