轻重链剖分主要运用线段树维护路径上点权的修改
链接:https://www.luogu.com.cn/problem/P3384
思路:线段树维护点权和即可,支持区间修改和查询
#include <bits/stdc++.h>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=1e5+10;
int n,m,r,mod;
vector<int> e[maxn];
int w[maxn],depth[maxn],fa[maxn],son[maxn],num[maxn];
int start[maxn],times,id[maxn],top[maxn];
int head[maxn],cnt;
struct Edge
{
int to,nxt;
}edges[maxn<<1];
void add(int u,int v)
{
edges[++cnt].to=v;
edges[cnt].nxt=head[u];
head[u]=cnt;
}
ll st[maxn<<2],lazy[maxn<<2];
void pushUp(int rt)
{
st[rt]=(st[ls]+st[rs])%mod;
}
void pushDown(int rt,int L,int R)
{
if(lazy[rt])
{
int mid=(L+R)>>1;
st[ls]=(st[ls]+lazy[rt]*(mid-L+1))%mod;
st[rs]=(st[rs]+lazy[rt]*(R-mid))%mod;
lazy[ls]+=lazy[rt];
lazy[rs]+=lazy[rt];
lazy[rt]=0;
}
}
void build(int rt,int l,int r)
{
lazy[rt]=0;
if(l==r)
{
st[rt]=w[id[l]]%mod;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushUp(rt);
}
int query(int rt,int l,int r,int L,int R)
{
if(l<=L&&R<=r)
return st[rt];
pushDown(rt,L,R);
int mid=(L+R)>>1;
int ans=0;
if(l<=mid) ans+=query(ls,l,r,L,mid);
if(r>mid) ans+=query(rs,l,r,mid+1,R);
return ans%mod;
}
void update(int rt,int l,int r,int L,int R,int val)
{
if(l<=L&&R<=r)
{
st[rt]+=(R-L+1)*val;
lazy[rt]+=val;
return;
}
pushDown(rt,L,R);
int mid=(L+R)>>1;
if(l<=mid) update(ls,l,r,L,mid,val);
if(r>mid) update(rs,l,r,mid+1,R,val);
pushUp(rt);
}
void dfs1(int u)
{
num[u]=1;
for(int i=head[u];i!=-1;i=edges[i].nxt)
{
int v=edges[i].to;
if(v==fa[u]) continue;
depth[v]=depth[u]+1;
fa[v]=u;
dfs1(v);
num[u]+=num[v];
if(num[v]>num[son[u]]) son[u]=v;
}
}
void dfs2(int u,int x)
{
start[u]=++times;
id[times]=u;
top[u]=x;
if(!son[u]) return;
dfs2(son[u],x);
for(int i=head[u];i!=-1;i=edges[i].nxt)
{
int v=edges[i].to;
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int qTree(int u)
{
return query(1,start[u],start[u]+num[u]-1,1,n);
}
void updTree(int u,int val)
{
update(1,start[u],start[u]+num[u]-1,1,n,val);
}
int qPath(int u,int v)
{
int ans=0;
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
ans+=query(1,start[top[u]],start[u],1,n);
ans%=mod;
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
ans+=query(1,start[u],start[v],1,n);
return ans%mod;
}
void updPath(int u,int v,int val)
{
val%=mod;
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
update(1,start[top[u]],start[u],1,n,val);
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
update(1,start[u],start[v],1,n,val);
}
int main()
{
memset(head,-1,sizeof(head)),cnt=0;
scanf("%d%d%d%d",&n,&m,&r,&mod);
for(int i=1;i<=n;++i) scanf("%d",&w[i]);
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs1(r);
dfs2(r,r);
build(1,1,n);
int op,u,v,dx;
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d",&u,&v,&dx);
updPath(u,v,dx);
}
else if(op==2)
{
scanf("%d%d",&u,&v);
printf("%d\n",qPath(u,v));
}
else if(op==3)
{
scanf("%d%d",&u,&dx);
updTree(u,dx);
}
else
{
scanf("%d",&u);
printf("%d\n",qTree(u));
}
}
return 0;
}
链接:https://www.luogu.com.cn/problem/P2590
思路:线段树维护单点修改、区间最大值查询、区间和查询
#include <bits/stdc++.h>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=3e4+10,inf=2e9;
int n,m;
int sum[maxn<<2],mx[maxn<<2],w[maxn];
vector<int> e[maxn];
int depth[maxn],fa[maxn],son[maxn],num[maxn];
int start[maxn],id[maxn],top[maxn],times;
void dfs1(int u)
{
num[u]=1;
for(auto v: e[u])
{
if(v==fa[u]) continue;
fa[v]=u;
depth[v]=depth[u]+1;
dfs1(v);
num[u]+=num[v];
if(num[v]>num[son[u]]) son[u]=v;
}
}
void dfs2(int u,int x)
{
start[u]=++times;
id[times]=u;
top[u]=x;
if(!son[u]) return;
dfs2(son[u],x);
for(auto v: e[u])
{
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void pushUp(int rt)
{
sum[rt]=sum[ls]+sum[rs];
mx[rt]=max(mx[ls],mx[rs]);
}
void build(int rt,int L,int R)
{
if(L==R)
{
sum[rt]=w[id[L]];
mx[rt]=w[id[L]];
return;
}
int mid=(L+R)>>1;
build(ls,L,mid);
build(rs,mid+1,R);
pushUp(rt);
}
void update(int rt,int p,int L,int R,int val)
{
if(L==R)
{
sum[rt]=val;
mx[rt]=val;
return;
}
int mid=(L+R)>>1;
if(p<=mid) update(ls,p,L,mid,val);
if(p>mid) update(rs,p,mid+1,R,val);
pushUp(rt);
}
int queryMax(int rt,int l,int r,int L,int R)
{
if(l<=L&&R<=r)
return mx[rt];
int ans=-inf;
int mid=(L+R)>>1;
if(l<=mid) ans=max(ans,queryMax(ls,l,r,L,mid));
if(r>mid) ans=max(ans,queryMax(rs,l,r,mid+1,R));
return ans;
}
int querySum(int rt,int l,int r,int L,int R)
{
if(l<=L&&R<=r)
return sum[rt];
int ans=0;
int mid=(L+R)>>1;
if(l<=mid) ans+=querySum(ls,l,r,L,mid);
if(r>mid) ans+=querySum(rs,l,r,mid+1,R);
return ans;
}
int qPathMax(int u,int v)
{
int ans=-inf;
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
ans=max(ans,queryMax(1,start[top[u]],start[u],1,n));
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
ans=max(ans,queryMax(1,start[u],start[v],1,n));
return ans;
}
int qPathSum(int u,int v)
{
int ans=0;
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
ans+=querySum(1,start[top[u]],start[u],1,n);
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
ans+=querySum(1,start[u],start[v],1,n);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;++i) scanf("%d",&w[i]);
dfs1(1);
dfs2(1,1);
build(1,1,n);
scanf("%d",&m);
string op;
int u,v,t;
while(m--)
{
cin>>op;
if(op=="QMAX")
{
scanf("%d%d",&u,&v);
printf("%d\n",qPathMax(u,v));
}
else if(op=="QSUM")
{
scanf("%d%d",&u,&v);
printf("%d\n",qPathSum(u,v));
}
else
{
scanf("%d%d",&u,&t);
update(1,start[u],1,n,t);
}
}
return 0;
}
坑点:线段树写岔了。一开始 lazy 设置成 0 不会往下更新。
#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=1e5+10;
int n,m;
vector<int> e[maxn];
int depth[maxn],fa[maxn],son[maxn],num[maxn];
int start[maxn],top[maxn],id[maxn],times;
int st[maxn<<2],lazy[maxn<<2];
void dfs1(int u)
{
num[u]=1;
for(auto v: e[u])
{
if(v==fa[u]) continue;
depth[v]=depth[u]+1;
fa[v]=u;
dfs1(v);
num[u]+=num[v];
if(num[v]>num[son[u]]) son[u]=v;
}
}
void dfs2(int u,int x)
{
start[u]=++times;
id[times]=u;
top[u]=x;
if(!son[u]) return;
dfs2(son[u],x);
for(auto v: e[u])
{
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void pushUp(int rt)
{
st[rt]=st[ls]+st[rs];
}
void pushDown(int rt,int L,int R)
{
if(lazy[rt]!=-1)
{
int mid=(L+R)>>1;
st[ls]=(mid-L+1)*lazy[rt];
st[rs]=(R-mid)*lazy[rt];
lazy[ls]=lazy[rt];
lazy[rs]=lazy[rt];
lazy[rt]=-1;
}
}
int query(int rt,int l,int r,int L,int R)
{
if(l<=L&&R<=r) return st[rt];
pushDown(rt,L,R);
int mid=(L+R)>>1;
int ans=0;
if(l<=mid) ans+=query(ls,l,r,L,mid);
if(r>mid) ans+=query(rs,l,r,mid+1,R);
return ans;
}
void update(int rt,int l,int r,int L,int R,int val)
{
if(l<=L&&R<=r)
{
st[rt]=(R-L+1)*val;
lazy[rt]=val;
return;
}
pushDown(rt,L,R);
int mid=(L+R)>>1;
if(l<=mid) update(ls,l,r,L,mid,val);
if(r>mid) update(rs,l,r,mid+1,R,val);
pushUp(rt);
}
void updPath(int u,int v,int dx)
{
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
update(1,start[top[u]],start[u],1,n,dx);
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
update(1,start[u],start[v],1,n,dx);
}
int qPathIn(int u,int v)
{
int len=0,ans=0;
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
len+=start[u]-start[top[u]]+1;
ans+=query(1,start[top[u]],start[u],1,n);
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
len+=start[v]-start[u]+1;
ans+=query(1,start[u],start[v],1,n);
return len-ans;
}
int main()
{
memset(lazy,-1,sizeof(lazy));
scanf("%d",&n);
for(int i=2;i<=n;++i)
{
int u;
scanf("%d",&u);
u++;
e[u].push_back(i);
e[i].push_back(u);
}
dfs1(1);
dfs2(1,1);
scanf("%d",&m);
string op;
int u;
while(m--)
{
cin>>op;
scanf("%d",&u);
u++;
if(op=="install")
{
printf("%d\n",qPathIn(1,u));
updPath(1,u,1);
}
else
{
printf("%d\n",query(1,start[u],start[u]+num[u]-1,1,n));
update(1,start[u],start[u]+num[u]-1,1,n,0);
}
}
return 0;
}
题意:维护线段树支持树上单点颜色的修改,以及颜色段数的询问
思路:询问路径上点的不同颜色段数,需要维护路径左右两端的上一个颜色,last1和last2,当 u 往上走时 更新last1,并且查询query1需要从后往前查询。当 v 往上走时,更新last2,查询query2也是从后往前。当 u 和 v 在同一条链上时,继续判断谁在上,u在上,那么就调用 query2,让 last2 从后往前更新。v 在上同理。最后判断一下last1和last2的颜色,相同需要减 1 。
#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=1e5+10;
int n,m;
vector<int> e[maxn];
int depth[maxn],fa[maxn],son[maxn],num[maxn];
int start[maxn],top[maxn],id[maxn],times;
int st[maxn<<2],lazy[maxn<<2],w[maxn];
void dfs1(int u)
{
num[u]=1;
for(auto v: e[u])
{
if(v==fa[u]) continue;
depth[v]=depth[u]+1;
fa[v]=u;
dfs1(v);
num[u]+=num[v];
if(num[v]>num[son[u]]) son[u]=v;
}
}
void dfs2(int u,int x)
{
start[u]=++times;
id[times]=u;
top[u]=x;
if(!son[u]) return;
dfs2(son[u],x);
for(auto v: e[u])
{
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void pushDown(int rt)
{
if(st[rt]!=-1)
{
st[ls]=st[rt];
st[rs]=st[rt];
st[rt]=-1;
}
}
void build(int rt,int L,int R)
{
if(L==R)
{
st[rt]=w[id[L]];
return;
}
int mid=(L+R)>>1;
build(ls,L,mid);
build(rs,mid+1,R);
}
int res,last1,last2;
void query1(int rt,int l,int r,int L,int R)
{
if(st[rt]!=-1)
{
if(last1!=st[rt])
res++;
last1=st[rt];
return;
}
if(L==R)
{
last1=st[rt];
return;
}
pushDown(rt);
int mid=(L+R)>>1;
if(r>mid) query1(rs,l,r,mid+1,R);
if(l<=mid) query1(ls,l,r,L,mid);
}
void query2(int rt,int l,int r,int L,int R)
{
if(st[rt]!=-1)
{
if(last2!=st[rt])
res++;
last2=st[rt];
return;
}
if(L==R)
{
last2=st[rt];
return;
}
pushDown(rt);
int mid=(L+R)>>1;
if(r>mid) query2(rs,l,r,mid+1,R);
if(l<=mid) query2(ls,l,r,L,mid);
}
void update(int rt,int l,int r,int L,int R,int val)
{
if(l<=L&&R<=r)
{
st[rt]=val;
return;
}
pushDown(rt);
int mid=(L+R)>>1;
if(l<=mid) update(ls,l,r,L,mid,val);
if(r>mid) update(rs,l,r,mid+1,R,val);
}
void updPath(int u,int v,int dx)
{
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
update(1,start[top[u]],start[u],1,n,dx);
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
update(1,start[u],start[v],1,n,dx);
}
int qPath(int u,int v)
{
int ans=0;
last1=last2=-1;
while(top[u]!=top[v])
{
if(depth[top[u]]>depth[top[v]])
{
res=0;
query1(1,start[top[u]],start[u],1,n);
ans+=res;
u=fa[top[u]];
}
else
{
res=0;
query2(1,start[top[v]],start[v],1,n);
ans+=res;
v=fa[top[v]];
}
}
if(depth[u]>depth[v])
{
res=0;
query1(1,start[v],start[u],1,n);
ans+=res;
if(last2==last1) ans--;
}
else
{
res=0;
query2(1,start[u],start[v],1,n);
ans+=res;
if(last2==last1) ans--;
}
return ans;
}
int main()
{
memset(st,-1,sizeof(st));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&w[i]);
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1);
dfs2(1,1);
build(1,1,n);
scanf("%d",&m);
char op[10];
int a,b,c;
while(m--)
{
scanf("%s",op);
if(op[0]=='Q')
{
scanf("%d%d",&a,&b);
printf("%d\n",qPath(a,b));
}
else
{
scanf("%d%d%d",&a,&b,&c);
updPath(a,b,c);
}
}
return 0;
}
题意:每次根变了之后,查询的范围也不一样
思路:操作2是路径操作与根无关。操作3每次都与现在根的位置有关。
#include <bits/stdc++.h>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=1e5+10;
int n,m,capital;
vector<int> e[maxn];
int fa[maxn],son[maxn],depth[maxn],num[maxn];
int start[maxn],id[maxn],top[maxn],times;
int st[maxn<<2],w[maxn],lazy[maxn<<2];
void dfs1(int u)
{
num[u]=1;
for(auto v: e[u])
{
if(v==fa[u]) continue;
depth[v]=depth[u]+1;
fa[v]=u;
dfs1(v);
num[u]+=num[v];
if(num[v]>num[son[u]]) son[u]=v;
}
}
void dfs2(int u,int x)
{
start[u]=++times;
id[times]=u;
top[u]=x;
if(!son[u]) return;
dfs2(son[u],x);
for(auto v: e[u])
{
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void pushUp(int rt)
{
st[rt]=min(st[ls],st[rs]);
}
void pushDown(int rt)
{
if(lazy[rt])
{
st[ls]=lazy[rt];
st[rs]=lazy[rt];
lazy[ls]=lazy[rt];
lazy[rs]=lazy[rt];
lazy[rt]=0;
}
}
void build(int rt,int L,int R)
{
if(L==R)
{
st[rt]=w[id[L]];
return;
}
int mid=(L+R)>>1;
build(ls,L,mid);
build(rs,mid+1,R);
pushUp(rt);
}
void update(int rt,int l,int r,int L,int R,int val)
{
if(l<=L&&R<=r)
{
st[rt]=val;
lazy[rt]=val;
return;
}
pushDown(rt);
int mid=(L+R)>>1;
if(l<=mid) update(ls,l,r,L,mid,val);
if(r>mid) update(rs,l,r,mid+1,R,val);
pushUp(rt);
}
ll query(int rt,int l,int r,int L,int R)
{
if(l<=L&&R<=r)
return st[rt];
pushDown(rt);
int mid=(L+R)>>1;
ll ans=(1ll<<31);
if(l<=mid) ans=min(ans,query(ls,l,r,L,mid));
if(r>mid) ans=min(ans,query(rs,l,r,mid+1,R));
return ans;
}
void updPath(int u,int v,int dx)
{
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]])swap(u,v);
update(1,start[top[u]],start[u],1,n,dx);
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
update(1,start[u],start[v],1,n,dx);
}
int lca(int u,int v)
{
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
return u;
}
int qTree(int u)
{
if(u==capital) return query(1,start[1],start[1]+num[1]-1,1,n);
else if(lca(u,capital)==u)
{
int x=capital;
for(auto v: e[u])
{
if(v==fa[u]) continue;
if(lca(v,capital)==v)
{
x=v;
break;
}
}
ll ans=(1ll<<31);
ans=min(ans,query(1,1,start[x]-1,1,n));
ans=min(ans,query(1,start[x]+num[x],n,1,n));
return ans;
}
else return query(1,start[u],start[u]+num[u]-1,1,n);
}
//也可以使用倍增的方法找 v ,不过要将dfs1中的fa[u]变成fa[u][i]这个数组
int qTree2(int u)
{
if(u==capital) return query(1,start[1],start[1]+num[1]-1,1,n);
else if(lca(u,capital)==u)
{
int v=capital;
int dis=depth[capital]-depth[u]-1;
for(int i=0;i<=20;++i)
if(dis>>i&1)
v=fa[v][i];
ll ans=(1ll<<31);
ans=min(ans,query(1,1,start[v]-1,1,n));
ans=min(ans,query(1,start[v]+num[v],n,1,n));
return ans;
}
else return query(1,start[u],start[u]+num[u]-1,1,n);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;++i) scanf("%d",&w[i]);
scanf("%d",&capital);
dfs1(1);
dfs2(1,1);
build(1,1,n);
int op,id,x,y,v;
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&id);
capital=id;
}
else if(op==2)
{
scanf("%d%d%d",&x,&y,&v);
updPath(x,y,v);
}
else if(op==3)
{
scanf("%d",&id);
printf("%d\n",qTree(id));
}
}
return 0;
}