线段树 BFS序
题目大意: 一个基环树,有点权。有询问和修改操作。修改有三个参数 u , k , d u,k,d u,k,d,表示把距离 u u u不超过 k k k(包括自己,下同)的点都加上 d d d。查询有两个参数 u , k u,k u,k,表示求距离 u u u不超过 k k k的点权和。 n ≤ 1 e 5 , k ≤ 2 n\leq 1e5,k\leq 2 n≤1e5,k≤2。
先别管那条非树边。当 k = 1 k=1 k=1的时候是维护它爸爸和它儿子,当 k = 2 k=2 k=2的时候还要维护它爷爷、它兄弟和它孙子。
我们对这棵树求一遍BFS序,这样当 k = 1 k=1 k=1时要维护的就是两个点和一个区间,当 k = 2 k=2 k=2时就是两个点和三个区间。线段树维护即可。对非树边的影响特殊处理。
具体实现比较繁琐,详见代码:
#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
typedef long long LL;
struct edge{ int nxt,to; }ed[N<<1];
struct tree{ int l,r; LL x,f; }t[N<<2];
int n,m,k,ti,v1,v2,h[N],fa[N],in[N],l[N][3],r[N][3];
queue <int> q; bool f[N],f1[N],f2[N];
//l/r[x][0]存它儿子的区间,[x][1]存它孙子的区间,[x][2]存它兄弟的区间
//f1/f2表示到它距离为1的点中有没有非树边连的两个点
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0,f=1; char ch=readc();
while (!isdigit(ch)&&ch!='M'&&ch!='Q'){ if (ch=='-') f=-1; ch=readc(); }
if (ch=='M'||ch=='Q') return ch;
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x*f;
}
F void bfs(){
for (int i=0;i<=n;i++) l[i][0]=1e9;
fa[1]=0,f[1]=true,q.push(1);
while (!q.empty()){
int x=q.front(); in[x]=++ti,q.pop();
r[fa[x]][0]=max(r[fa[x]][0],ti);
l[fa[x]][0]=min(l[fa[x]][0],ti);
for (int i=h[x],v;i;i=ed[i].nxt)
if ((v=ed[i].to)!=fa[x]&&!f[v])
fa[v]=x,f[v]=true,q.push(v);
}
}
#define add(x,y) ed[++k]=(edge){h[x],y},h[x]=k
int findfa(int x){ return x==fa[x]?x:fa[x]=findfa(fa[x]); }
F void pshp(int x){ t[x].x=t[x<<1].x+t[x<<1|1].x; }
F void add1(int x,LL w){ t[x].x+=w*(t[x].r-t[x].l+1),t[x].f+=w; }
F void pshd(int x){ if (t[x].f) add1(x<<1,t[x].f),add1(x<<1|1,t[x].f),t[x].f=0; }
void build(int x,int l,int r){
t[x]=(tree){l,r,0,0}; if (l==r) return; int mid=l+r>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
}
void mdfy(int x,int l,int r,LL w){
if (t[x].l>r||t[x].r<l) return;
if (t[x].l>=l&&t[x].r<=r) return add1(x,w);
pshd(x),mdfy(x<<1,l,r,w),mdfy(x<<1|1,l,r,w),pshp(x);
}
LL srch(int x,int l,int r){
if (t[x].l>r||t[x].r<l) return 0;
if (t[x].l>=l&&t[x].r<=r) return t[x].x;
return pshd(x),srch(x<<1,l,r)+srch(x<<1|1,l,r);
}
F bool pd(int y,int x){
if (fa[x]==y||fa[fa[x]]==y) return false;
for (int i=0;i<3;i++)
if (in[y]>=l[x][i]&&in[y]<=r[x][i]) return false;
return true;
}
F void nsrt(){//分类讨论修改范围,下同
int x=_read(),k=_read(),d=_read();
bool ff1=true,ff2=true;
if (k==0) mdfy(1,in[x],in[x],d);
if (k==1){
mdfy(1,in[x],in[x],d),mdfy(1,l[x][0],r[x][0],d);
if (fa[x]) mdfy(1,in[fa[x]],in[fa[x]],d);
if (x==v1) mdfy(1,in[v2],in[v2],d);
if (x==v2) mdfy(1,in[v1],in[v1],d);
}
if (k==2){
for (int i=0;i<3;i++) mdfy(1,l[x][i],r[x][i],d);
if (x==v1){//对非树边连的两个点特判
if (pd(v2,x)) mdfy(1,in[v2],in[v2],d);
if (pd(fa[v2],x)) mdfy(1,in[fa[v2]],in[fa[v2]],d);
mdfy(1,l[v2][0],r[v2][0],d),ff1=fa[fa[x]]!=v2,ff2=fa[fa[fa[x]]]!=v2;
//要修改到另一个点的距离为1的点,注意不要重复
}
if (x==v2){
if (pd(v1,x)) mdfy(1,in[v1],in[v1],d);
if (pd(fa[v1],x)) mdfy(1,in[fa[v1]],in[fa[v1]],d);
mdfy(1,l[v1][0],r[v1][0],d),ff1=fa[fa[x]]!=v1,ff2=fa[fa[fa[x]]]!=v1;
}
if (fa[x]&&ff1) mdfy(1,in[fa[x]],in[fa[x]],d);
if (fa[fa[x]]&&ff2) mdfy(1,in[fa[fa[x]]],in[fa[fa[x]]],d);
if (f1[x]&&pd(v2,x)) mdfy(1,in[v2],in[v2],d);
if (f2[x]&&pd(v1,x)) mdfy(1,in[v1],in[v1],d);
}
}
F void find(){
int x=_read(),k=_read(); LL ans=0;
bool ff1=true,ff2=true;
if (k==0) ans+=srch(1,in[x],in[x]);
if (k==1){
ans+=srch(1,in[x],in[x])+srch(1,l[x][0],r[x][0]);
if (fa[x]) ans+=srch(1,in[fa[x]],in[fa[x]]);
if (x==v1) ans+=srch(1,in[v2],in[v2]);
if (x==v2) ans+=srch(1,in[v1],in[v1]);
}
if (k==2){
for (int i=0;i<3;i++) ans+=srch(1,l[x][i],r[x][i]);
if (x==v1){
if (pd(v2,x)) ans+=srch(1,in[v2],in[v2]);
if (pd(fa[v2],x)) ans+=srch(1,in[fa[v2]],in[fa[v2]]);
ans+=srch(1,l[v2][0],r[v2][0]),ff1=fa[fa[x]]!=v2,ff2=fa[fa[fa[x]]]!=v2;
}
if (x==v2){
if (pd(v1,x)) ans+=srch(1,in[v1],in[v1]);
if (pd(fa[v1],x)) ans+=srch(1,in[fa[v1]],in[fa[v1]]);
ans+=srch(1,l[v1][0],r[v1][0]),ff1=fa[fa[x]]!=v1,ff2=fa[fa[fa[x]]]!=v1;
}
if (fa[x]&&ff1) ans+=srch(1,in[fa[x]],in[fa[x]]);
if (fa[fa[x]]&&ff2) ans+=srch(1,in[fa[fa[x]]],in[fa[fa[x]]]);
if (f1[x]&&pd(v2,x)) ans+=srch(1,in[v2],in[v2]);
if (f2[x]&&pd(v1,x)) ans+=srch(1,in[v1],in[v1]);
}
printf("%lld\n",ans);
}
int main(){
for (int T=_read();T;T--){
n=_read(),k=ti=0;
memset(h,0,sizeof(h)),memset(f,0,sizeof(f));
memset(l,0,sizeof(l)),memset(r,0,sizeof(r));
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=1;i<=n;i++){
int x=_read(),y=_read(),fx,fy;
if ((fx=findfa(x))==(fy=findfa(y))) v1=x,v2=y;
else add(x,y),add(y,x),fa[fx]=fy;
}
bfs(),m=_read();
for (int x=1;x<=n;x++){
l[x][1]=1e9,f1[x]=fa[x]==v1,f2[x]=fa[x]==v2;
for (int i=h[x],v;i;i=ed[i].nxt){
if ((v=ed[i].to)==fa[x]) continue;
f1[x]|=(v=ed[i].to)==v1,f2[x]|=v==v2;
l[x][1]=min(l[x][1],l[v][0]);
r[x][1]=max(r[x][1],r[v][0]);
}
l[x][2]=l[fa[x]][0],r[x][2]=r[fa[x]][0];
}
for (build(1,1,n);m;m--) _read()=='M'?nsrt():find();
}
return 0;
}