动态开点线段树
阅读本篇请先学习线段树。
动态开点线段树是一类特殊的线段树,与普通的线段树不同的是,每一个节点的左右儿子不是该点编号的两倍和两倍加一,而是现加出来的。
我们用lson[u]记录u的左儿子,rson[u]记录u的右儿子(博主不用define于是用ll[u]和rr[u]),其他就不赘述了,以下是建树的代码:
void build(long long u,long long l1,long long r1) { l[u]=l1; r[u]=r1; if (l1==r1) { z[u]=a[l1]; return; } cnt++; ll[u]=cnt;//用cnt记录点的个数 build(ll[u],l1,(l1+r1)/2); cnt++; rr[u]=cnt; build(rr[u],(l1+r1)/2+1,r1); z[u]=z[ll[u]]+z[rr[u]]; }
完整代码如下:
void xiafang(long long u) { z[ll[u]]=c[u]*(r[ll[u]]-l[ll[u]]+1); c[ll[u]]+=c[u]; z[rr[u]]=c[u]*(r[rr[u]]-l[rr[u]]+1); c[rr[u]]+=c[u]; c[u]=0; } void build(long long u,long long l1,long long r1) { l[u]=l1; r[u]=r1; if (l1==r1) { z[u]=a[l1]; return; } cnt++; ll[u]=cnt; build(ll[u],l1,(l1+r1)/2); cnt++; rr[u]=cnt; build(rr[u],(l1+r1)/2+1,r1); z[u]=z[ll[u]]+z[rr[u]]; } void jia(long long u,long long l1,long long r1,long long k) { if (l[u]>r1||r[u]<l1) return; if (l[u]>=l1&&r[u]<=r1) { z[u]+=k*(r[u]-l[u]+1); c[u]+=k; return; } if (c[u]) xiafang(u); jia(ll[u],l1,r1,k); jia(rr[u],l1,r1,k); z[u]=z[ll[u]]+z[rr[u]]; } long long qui(long long u,long long l1,long long r1) { if (l[u]>r1||r[u]<l1) return 0; if (l[u]>=l1&&r[u]<=r1) return z[u]; if (c[u]) xiafang(u); return (qui(ll[u],l1,r1)+qui(rr[u],l1,r1))%998244353; }
其实只要将u*2和u*2+1与ll[u],rr[u]换一下其实就好了。