题目大意
给出一个n个点n条边的图,有两种操作:
1.Uptate x k d 将所有距离x距离不超过k的所有节点加d
2.Query x k 统计所有距离x距离不超过k的所有节点
n ≤ 1 0 5 , 0 ≤ k ≤ 2 n\le 10^5,0\le k \le 2 n≤105,0≤k≤2
解题分析
基环树常用套路:将整个环当做一个根节点,然后建树。
然后用BFS建树的时候可以得到一个进队列的顺序,称之为BFS序(相对的也有DFS序)。
那么自己画个图就会发现一个节点的儿子节点和孙子节点在BFS序上都在一个区间内,那么直接区间修改,用线段树维护,但这道题不是树而是基环树,所以需要分类讨论一波,具体见代码。
示例代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100005,maxe=200005;
int n,q,tot,tst,son[maxe],nxt[maxe],lnk[maxn],a[maxn],fa[maxn],BCC[maxn];
int hed,til,ti,que[maxn],id[maxn],Ls[maxn],Rs[maxn],Lg[maxn],Rg[maxn];
bool vs[maxn]; char st[10];
struct Seg{
int tL[maxn<<2],tR[maxn<<2],tg[maxn<<2];
LL sum[maxn<<2];
inline int getL(int x){return x>1?x-1:a[0];}
inline int getR(int x){return x<a[0]?x+1:1;}
void Build(int L,int R,int p){
tL[p]=L; tR[p]=R; sum[p]=tg[p]=0; if (L==R) return;
int mid=L+(R-L>>1); Build(L,mid,p<<1); Build(mid+1,R,p<<1|1);
}
inline void Pushup(int x){sum[x]=sum[x<<1]+sum[x<<1|1];}
inline void Addtag(int x,int tem){sum[x]+=(LL)(tR[x]-tL[x]+1)*tem; tg[x]+=tem;}
inline void Pushdown(int x){if (tg[x]){Addtag(x<<1,tg[x]); Addtag(x<<1|1,tg[x]); tg[x]=0;}}
inline void Insert(int L,int R,int x,int tem){
if (tL[x]>R||tR[x]<L) return; if (L<=tL[x]&&tR[x]<=R) {Addtag(x,tem); return;}
int mid=tL[x]+(tR[x]-tL[x]>>1); Pushdown(x);
if (R<=mid) Insert(L,R,x<<1,tem); else if (L>mid) Insert(L,R,x<<1|1,tem);
else {Insert(L,mid,x<<1,tem); Insert(mid+1,R,x<<1|1,tem);} Pushup(x);
}
LL Ask(int L,int R,int x){
if (tL[x]>R||tR[x]<L) return 0; if (L<=tL[x]&&tR[x]<=R) return sum[x];
int mid=tL[x]+(tR[x]-tL[x]>>1); Pushdown(x);
if (R<=mid) return Ask(L,R,x<<1); else if (L>mid) return Ask(L,R,x<<1|1);
else return Ask(L,mid,x<<1)+Ask(mid+1,R,x<<1|1);
}
inline LL Askone(int x){return Ask(x,x,1);}
inline void Addone(int x,int tem){Insert(x,x,1,tem);}
void Uptate(int x,int k,int tem){
Addone(id[x],tem);
if (k>=1){
Insert(Ls[x],Rs[x],1,tem);
if (!BCC[x]) Addone(id[fa[x]],tem);
else {Addone(id[a[getL(BCC[x])]],tem); Addone(id[a[getR(BCC[x])]],tem);}
}
if (k>=2){
Insert(Lg[x],Rg[x],1,tem);
if (!BCC[x]){
Addone(id[x],-tem); Insert(Ls[fa[x]],Rs[fa[x]],1,tem);
int p=BCC[fa[x]]; if (!p) Addone(id[fa[fa[x]]],tem);
else {Addone(id[a[getL(p)]],tem); Addone(id[a[getR(p)]],tem);}
}else{
int BL=getL(BCC[x]),BR=getR(BCC[x]); Insert(Ls[a[BL]],Rs[a[BL]],1,tem); Insert(Ls[a[BR]],Rs[a[BR]],1,tem);
if (getL(BL)!=BR) Addone(id[a[getL(BL)]],tem);
if (getR(BR)!=BL&&getR(BR)!=getL(BL)) Addone(id[a[getR(BR)]],tem);
}
}
}
LL Query(int x,int k){
LL ans=Askone(id[x]); //自己节点
if (k>=1){
ans+=Ask(Ls[x],Rs[x],1); //儿子区间
if (!BCC[x]) ans+=Askone(id[fa[x]]); //如果不在环上,加父节点
else ans+=Askone(id[a[getL(BCC[x])]])+Askone(id[a[getR(BCC[x])]]);
//如果在环上,加环上的左右节点
}
if (k>=2){
ans+=Ask(Lg[x],Rg[x],1);//孙子区间
if (!BCC[x]){
ans+=Ask(Ls[fa[x]],Rs[fa[x]],1)-Askone(id[x]);//兄弟节点
int p=BCC[fa[x]]; if (!p) ans+=Askone(id[fa[fa[x]]]);
else ans+=Askone(id[a[getL(p)]])+Askone(id[a[getR(p)]]);
}else{
int BL=getL(BCC[x]),BR=getR(BCC[x]); ans+=Ask(Ls[a[BL]],Rs[a[BL]],1)+Ask(Ls[a[BR]],Rs[a[BR]],1);
if (getL(BL)!=BR) ans+=Askone(id[a[getL(BL)]]);
if (getR(BR)!=BL&&getR(BR)!=getL(BL)) ans+=Askone(id[a[getR(BR)]]);
}//左边的左边和右边的右边,要防取重
}
return ans;
}
}T;
inline void readi(int &x){
x=0; char ch=getchar(),lst='+';
while ('0'>ch||ch>'9') {lst=ch; ch=getchar();}
while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
if (lst=='-') x=-x;
}
void _add(int x,int y){son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;}
void getBCC(int x,int y){for (int i=x;i!=y;i=fa[i]){a[++a[0]]=i; BCC[i]=a[0];} a[++a[0]]=y; BCC[y]=a[0];} //求环
void _dfs(int x,int dad){
vs[x]=true;
for (int j=lnk[x];j;j=nxt[j])
if (son[j]!=dad) if (!vs[son[j]]){fa[son[j]]=x; _dfs(son[j],x);} else if (!BCC[x]) getBCC(x,son[j]);
}
void _bfs(int S){ //求BFS序
hed=0; til=1; que[1]=S; id[S]=++ti;
while (hed!=til){
int x=que[++hed]; Ls[x]=ti+1;
for (int j=lnk[x];j;j=nxt[j])
if (!id[son[j]]&&!BCC[son[j]]){fa[son[j]]=x; que[++til]=son[j]; id[son[j]]=++ti;}
Rs[x]=ti; //求x的儿子区间
}
for (int i=1;i<=til;i++){
int x=que[i]; Lg[x]=2e9; Rg[x]=-2e9;
for (int j=lnk[x];j;j=nxt[j])
if (id[son[j]]>id[x]&&!BCC[son[j]]){Lg[x]=min(Lg[x],Ls[son[j]]); Rg[x]=max(Rg[x],Rs[son[j]]);} //求x的孙子区间
}
}
int main()
{
freopen("qtree0.in","r",stdin);
freopen("qtree0.out","w",stdout);
readi(tst);
while (tst--){
readi(n); tot=ti=a[0]=0;
memset(lnk,0,sizeof(lnk)); memset(vs,0,sizeof(vs));
memset(id,0,sizeof(id)); memset(BCC,0,sizeof(BCC));
for (int i=1,x,y;i<=n;i++){readi(x); readi(y); _add(x,y); _add(y,x);}
_dfs(1,0); for (int i=1;i<=a[0];i++) _bfs(a[i]); T.Build(1,ti,1);
readi(q);
for (int x,y,z;q;q--){
scanf("%s",st); readi(x); readi(y);
if (st[0]=='Q') printf("%lld\n",T.Query(x,y));
else {readi(z); T.Uptate(x,y,z);}
}
}
return 0;
}
话说回来如果只有区间修改的的话可以考虑树状数组,所以……这里就是个F(k)l(e)a(n)g(g)