线段树(板子)
线段树
- 单点修改,单点,区间查询
- 区间修改,单点,区间查询
单点修改
普通线段树
code
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lll long long
const int N = 1000001;
int n,m;
int a[N];
struct T
{
int l,r,data;
} tr[N<<2];
void pushup(int rt)
{
tr[rt].data=tr[ls].data+tr[rs].data;
}
void bui(int rt,int l,int r)
{
tr[rt].l=l; tr[rt].r=r;
if(l==r)
{
tr[rt].data=a[l];
return;//注意return
}
int mid=((lll)l+r)>>1;//注意long long
bui(ls,l,mid); bui(rs,mid+1,r);
pushup(rt);
}
void mdf(int rt,int x,int v)
{
if(tr[rt].l==tr[rt].r)
{
tr[rt].data+=v;
return;
}
int mid=((lll)tr[rt].l+tr[rt].r)>>1;
if(x<=mid) mdf(ls,x,v);
else mdf(rs,x,v);
pushup(rt);
}
int que(int rt,int l,int r)
{
if(l<=tr[rt].l&&r>=tr[rt].r)
return tr[rt].data;
int mid=((lll)tr[rt].l+tr[rt].r)>>1;
int res=0;
if(l<=mid) res+=que(ls,l,r);
if(r>mid) res+=que(rs,l,r);
return res;
}
区间修改
“卤煮”存一下更新,查询时再下放。
code
#define lll long long
#define ls (rt << 1)
#define rs (rt << 1 | 1)
const int N = 100005;
int n,a[N],m;
struct T
{
int l,r,data,lz;
} tr[N<<2];
void pushup(int rt)
{
tr[rt].data=tr[ls].data+tr[rs].data;
}
void pushdown(int rt)
{
if(tr[rt].lz!=0)
{
int lz=tr[rt].lz;
tr[rt].lz=0;
tr[ls].lz+=lz;
tr[rs].lz+=lz;
tr[ls].data+=lz*(tr[ls].r-tr[ls].l+1);
tr[rs].data+=lz*(tr[rs].r-tr[rs].l+1);
}
}
void bui(int rt,int l,int r)
{
tr[rt].l=l; tr[rt].r=r;
if(l==r)
{
tr[rt].data=a[l];
return ;
}
int mid=((lll)l+r)>>1;
bui(ls,l,mid); bui(rs,mid+1,r);
pushup(rt);
}
void mdf(int rt,int l,int r,int v)
{
if(l<=tr[rt].l&&r>=tr[rt].r)
{
tr[rt].lz+=v;
tr[rt].data+=v*(tr[rt].r-tr[rt].l+1);
return;
}
pushdown(rt);//注意回溯
int mid=((lll)tr[rt].l+tr[rt].r)>>1;
if(l<=mid) mdf(ls,l,r,v);
if(r>mid) mdf(rs,l,r,v);
pushup(rt);
}
int que(int rt,int l,int r)
{
if(l<=tr[rt].l&&r>=tr[rt].r)
return tr[rt].data;
pushdown(rt);
int mid=((lll)tr[rt].l+tr[rt].r)>>1;
int res=0;
if(l<=mid) res+=que(ls,l,r);
if(r>mid) res+=que(rs,l,r);
return res;
}
线段树就是打心态。
补充
\(pushup\) 可以返回结构体,对于查询时需要进行合并操作的,还有返回多个数据的 很方便。
例一 Alternating String
大致题意:一个 \(0~1\) 序列,定义“好序列”为相邻两位不同(如:\(0~1~0~1\) 或 \(1~0~1~0\))。
修改操作:区间 \(L~R\) \(0\) 变 \(1\) ,\(1\) 变 \(0\).
查询操作:区间 \(L~R\) 是否为“好”。
code
struct T
{
int l,r,ld,rd,lz;
bool ans;
} tr[N << 2];
T pushup(T r1,T r2)
{
T tree;
tree.l=r1.l; tree.r=r2.r;
tree.ld=r1.ld; tree.rd=r2.rd;
tree.lz=0;
if(r1.ans&&r2.ans)
{
if(r1.rd!=r2.ld) tree.ans=1;
else tree.ans=0;
}
else tree.ans=0;
return tree;
}
void pushdown(int rt)
{
if(tr[rt].lz)
{
tr[rt].lz = 0;
tr[ls].lz ^= 1; tr[rs].lz ^= 1;
tr[ls].ld ^= 1; tr[rs].rd ^= 1;
tr[rs].ld ^= 1; tr[ls].rd ^= 1;
}
}
void bui(int rt,int l,int r)
{
tr[rt].l=l; tr[rt].r=r;
if(l==r)
{
tr[rt].ans=1;
tr[rt].ld=tr[rt].rd=a[l];
return;
}
int mid=(l+r)>>1;
bui(ls,l,mid); bui(rs,mid+1,r);
int flag=tr[rt].lz;//“卤煮”处理的不太好,因为不能从下向上回溯。所以额外存一下。
tr[rt]=pushup(tr[ls],tr[rs]);
tr[rt].lz=flag;
}
void mdf(int rt,int l,int r)
{
if(l<=tr[rt].l&&r>=tr[rt].r)
{
tr[rt].lz ^= 1;
tr[rt].ld ^= 1;
tr[rt].rd ^= 1;
return;
}
pushdown(rt);
int mid=(tr[rt].l+tr[rt].r)>>1;
if(l<=mid) mdf(ls,l,r);
if(r>mid) mdf(rs,l,r);
int flag=tr[rt].lz;
tr[rt]=pushup(tr[ls],tr[rs]);
tr[rt].lz=flag;
}
T que(int rt,int l,int r)
{
if(l<=tr[rt].l&&r>=tr[rt].r)
return tr[rt];
pushdown(rt);
int mid=(tr[rt].l+tr[rt].r)>>1;
if(r<=mid) return que(ls,l,r);
if(mid<l) return que(rs,l,r);
return pushup(que(ls,l,r),que(rs,l,r));
}