线段树维护等差数列
线段树维护等差数列
先整个简单的Luogu1438 无聊的数列
这个确实简单,直接前缀和就行了......
真正的等差数列
上面那个就是个\(ZZ\)题,所以我们如何维护等差数列可以做到区间求和呢??
你发现等差数列是这样的:\(a,a+b,a+2b,...,a+nb\)
那要是这样的话,我们发现\(b\)的倍数是\(1,2,3,...\)
所以我们这样设计线段树\(1a_1,2a_2,3a_3,...\)
\(a\)就是我们线段树每一个节点的值,那么我们直接对线段树进行区间加,也就是对等差数列的区间加
于是你发现我们加的一定是公差,但是可能首项就不一样了,那么我们就再加一个\(b\)
维护多出来或者少的那一部分值,把首项变成正确的
于是一个带系数的线段树,和一个普通线段树,共同构成了等差数列
这两个当然可以放在同一个线段树里啦!!
所以我做这个第一个题就是BZOJ 3221
标记永久化,我记得了,主席树的区间修改+区间查询(也可以单点查询修改,好像标记永久化很方便的亚子,主要针对可加性的变化)
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
int s=0,t=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
return s*t;
}
const int N=1e6+5;
int n,q,lans;
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
int son[N],siz[N],dep[N],fa[N];
int top[N],dfn[N],idf[N],cnt;
void dfs_fi(int x){
son[x]=0;siz[x]=1;
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==fa[x])continue;
fa[y]=x;dep[y]=dep[x]+1;
dfs_fi(y);
siz[x]+=siz[y];
if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
}
}
void dfs_se(int x,int f){
top[x]=f;dfn[x]=++cnt;idf[cnt]=x;
if(son[x])dfs_se(son[x],f);
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==son[x]||y==fa[x])continue;
dfs_se(y,y);
}
}
int rz[N],rf[N],cur,tot;
const int de=18;
struct ZXS{
int sum[N*de],tag[N*de],taf[N*de];
int tp[N*de],ls[N*de],rs[N*de];
int seg;
int newpot(int x){
++seg;sum[seg]=sum[x];tag[seg]=tag[x];
taf[seg]=taf[x];tp[seg]=cur;
ls[seg]=ls[x];rs[seg]=rs[x];
return seg;
}
void ins(int &x,int l,int r,int ql,int qr,int v,int w){
if(ql>qr)return ;
x=newpot(x);
if(ql<=l&&r<=qr){
tag[x]+=v;taf[x]+=w;
return ;
}
int s=max(l,ql),t=min(r,qr);
sum[x]+=v*(s+t)*(t-s+1)/2+w*(t-s+1);
int mid=l+r>>1;
if(ql<=mid)ins(ls[x],l,mid,ql,qr,v,w);
if(qr>mid)ins(rs[x],mid+1,r,ql,qr,v,w);
return ;
}
int query(int x,int l,int r,int ql,int qr){
if(!x||ql>qr)return 0;
int s=max(l,ql),t=min(r,qr);
int ret=tag[x]*(s+t)*(t-s+1)/2+taf[x]*(t-s+1);
if(ql<=l&&r<=qr)return ret+sum[x];
int mid=l+r>>1;
if(ql<=mid)ret+=query(ls[x],l,mid,ql,qr);
if(qr>mid)ret+=query(rs[x],mid+1,r,ql,qr);
return ret;
}
}zs,fs;
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
void change(int x,int y,int a,int b){
int lca=LCA(x,y),dis=dep[x]+dep[y]-2*dep[lca]+1;
int sx=a;
while(top[x]!=top[lca]){
fs.ins(rf[cur],1,n,n-dfn[x]+1,n-dfn[top[x]]+1,b,sx-b*(n-dfn[x]+1));
sx+=b*(dfn[x]-dfn[top[x]]+1);
x=fa[top[x]];
}
fs.ins(rf[cur],1,n,n-dfn[x]+1,n-dfn[lca]+1,b,sx-b*(n-dfn[x]+1));
sx=a+b*(dis-1);
while(top[y]!=top[lca]){
zs.ins(rz[cur],1,n,dfn[top[y]],dfn[y],b,sx-b*(dfn[y]-dfn[top[y]])-b*dfn[top[y]]);
sx-=b*(dfn[y]-dfn[top[y]]+1);
y=fa[top[y]];
}
zs.ins(rz[cur],1,n,dfn[lca]+1,dfn[y],b,sx-b*(dfn[y]-dfn[lca]-1)-b*(dfn[lca]+1));
return ;
}
int query(int x,int y){
int lca=LCA(x,y),ret=0;
while(top[x]!=top[lca]){
ret+=fs.query(rf[cur],1,n,n-dfn[x]+1,n-dfn[top[x]]+1);
ret+=zs.query(rz[cur],1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
ret+=fs.query(rf[cur],1,n,n-dfn[x]+1,n-dfn[lca]+1);
ret+=zs.query(rz[cur],1,n,dfn[lca],dfn[x]);
while(top[y]!=top[lca]){
ret+=fs.query(rf[cur],1,n,n-dfn[y]+1,n-dfn[top[y]]+1);
ret+=zs.query(rz[cur],1,n,dfn[top[y]],dfn[y]);
y=fa[top[y]];
}
ret+=fs.query(rf[cur],1,n,n-dfn[y]+1,n-dfn[lca]);
ret+=zs.query(rz[cur],1,n,dfn[lca]+1,dfn[y]);
return ret;
}
signed main(){
n=read();q=read();
fo(i,2,n){
int x=read(),y=read();
add_edg(x,y);
add_edg(y,x);
}
dfs_fi(1);dfs_se(1,1);
while(q--){
char t[10];int x,y,a,b;
scanf("%s",t+1);
if(t[1]=='c'){
tot++;rz[tot]=zs.newpot(rz[cur]);
rf[tot]=fs.newpot(rf[cur]);cur=tot;
x=read()^lans,y=read()^lans,a=read(),b=read();
change(x,y,a,b);
}
if(t[1]=='q'){
x=read()^lans,y=read()^lans;
printf("%lld\n",lans=query(x,y));
}
if(t[1]=='l'){
cur=read()^lans;
}
}
}
然鹅一开始我写的时候就下放标记了,差点猝死
神奇的线段树,啥都能干!!!
QQ:2953174821