题目:
查询一个点可以转化为查询点分树上自己到根的路径上每个点对应范围答案。可用树状数组 f 。
但有重复,所以再开一个树状数组 g 记录上一层重心的含自己的那棵子树里各种距离的点值和。
查询的时候如果上一层的重心有贡献,就加上它的 f 的对应范围,再减去这一层的 g 的对应范围。这两个范围是一样的,因为这一层的 g 也是相对上一层重心的距离。
修改的时候枚举每层,改一下这一层的 f 和下一层的 g 。只是这样写比较方便而已。
可以用 rmq O(1) 求两点距离,就是欧拉序里两点之间深度最小的就是 LCA 的深度,所以用 ST 表查询之类的。
所有的树状数组一共应该是 nlogn 个点,所以自己开了 nlogn 的数组,给每个点一个 l 和 r 表示它们用的是哪一段。然后疯狂 TLE 。可能也是 query 和 mdfy 的地方写得不好。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#include#include #include #define il inline#define rg registerusing namespace std;const int N=1e5+5,K=17;int n,w[N],hd[N],xnt,to[N<<1],nxt[N<<1],ans;int tim,tot,dep[N],q[N<<1],ps[N],st[N][K+1],lg[N<<1],bin[K+5];int mn,rt,siz[N],pre[N],f[N*K],g[N*K<<1],fl[N],fr[N],gl[N],gr[N],ftp,gtp;bool vis[N];il int rdn(){ rg int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret;}int dg[20];il void wrt(int x){ if(!x){puts("0");return;} rg int tt=0;while(x)dg[++tt]=x%10,x/=10; for(;tt;tt--)putchar(dg[tt]+'0');puts("");}il int Mx(rg int a,rg int b){ return a>b?a:b;}il int Mn(rg int a,rg int b){ return a >1]+1;//lg[1]=0! bin[0]=1;for(rg int i=1;i<=lg[tot];i++)bin[i]=bin[i-1]<<1; for(rg int i=1;i<=tot;i++)st[i][0]=dep[q[i]]; for(rg int t=1;t<=lg[tot];t++) for(rg int i=1;i+bin[t]-1<=tot;i++) st[i][t]=Mn(st[i][t-1],st[i+bin[t-1]][t-1]);//bin[t-1]!!}il int pd(rg int x,rg int y){ rg int u=ps[x],v=ps[y];if(u>v)swap(u,v); rg int t=lg[v-u+1],d=Mn(st[u][t],st[v-bin[t]+1][t]); return dep[x]-d+dep[y]-d;}il void add(rg int x,rg int l,rg int r,rg int k){ for(rg int d=r-l;x<=d;x+=(x&-x))f[x+l]+=k;}il void addx(rg int x,rg int l,rg int r,rg int k){ for(rg int d=r-l;x<=d;x+=(x&-x))g[x+l]+=k;}il int qry(rg int x,rg int l,rg int r){ if(!x)return 0;rg int ret=0;for(;x;x-=(x&-x))ret+=f[x+l];return ret;}il int qryx(rg int x,rg int l,rg int r){rg int ret=0;for(;x;x-=(x&-x))ret+=g[x+l];return ret;}il void getrt(rg int cr,rg int fa,rg int s){ siz[cr]=1;rg int mx=0; for(rg int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa) { getrt(v,cr,s);siz[cr]+=siz[v];mx=Mx(mx,siz[v]); } mx=Mx(mx,s-siz[cr]); if(mx
看了半天题解,写成线段树版本。还是 TTT 。
如果给这份代码写上很多 inline 和 register ,就会在 bzoj 上 CE 。会报错 l+r>>1 位置没加括号,然而加上之后会没有错误信息地 CE 。也不知是怎么回事。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#include#include #include #define ls Ls[cr]#define rs Rs[cr]using namespace std;const int N=1e5+5,M=N*200,K=17;int n,w[N],hd[N],xnt,to[N<<1],nxt[N<<1],ans;int tot,dep[N],ps[N],st[N<<1][K+1],lg[N<<1],bin[K+5];//st[N<<1]int mn,rot,siz[N],pre[N],rt[N<<1],sm[M],Ls[M],Rs[M];bool vis[N];int rdn(){ int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret;}int dg[20];void wrt(int x){ if(!x){puts("0");return;} int tt=0;while(x)dg[++tt]=x%10,x/=10; for(;tt;tt--)putchar(dg[tt]+'0');puts("");}int Mx(int a,int b){ return a>b?a:b;}int Mn(int a,int b){ return a >1]+1;//lg[1]=0! bin[0]=1;for(int i=1;i<=lg[tot];i++)bin[i]=bin[i-1]<<1; for(int t=1;t<=lg[tot];t++) for(int i=1;i+bin[t]-1<=tot;i++) st[i][t]=Mn(st[i][t-1],st[i+bin[t-1]][t-1]);//bin[t-1]!!}int pd(int x,int y){ int u=ps[x],v=ps[y];if(u>v)swap(u,v); int t=lg[v-u+1],d=Mn(st[u][t],st[v-bin[t]+1][t]); return dep[x]-d+dep[y]-d;}void getrt(int cr,int fa,int s){ siz[cr]=1;int mx=0; for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa) { getrt(v,cr,s);siz[cr]+=siz[v];mx=Mx(mx,siz[v]); } mx=Mx(mx,s-siz[cr]); if(mx >1; if(R>mid)return sm[ls]+qry(mid+1,r,rs,R); return qry(l,mid,ls,R);}void mdfy(int l,int r,int &cr,int p,int k){ if(!cr)cr=++tot; sm[cr]+=k; if(l==r)return; int mid=l+r>>1; if(p<=mid)mdfy(l,mid,ls,p,k); else mdfy(mid+1,r,rs,p,k);}void query(int cr,int yd){ int x=cr,dis=yd; ans+=qry(0,n,rt[cr],dis); for(;pre[cr];cr=pre[cr]) { dis=yd-pd(x,pre[cr]); if(dis<0)continue; ans+=qry(0,n,rt[pre[cr]],dis); ans-=qry(0,n,rt[cr+n],dis); }}void mdfy(int cr,int k){ int x=cr,dis; mdfy(0,n,rt[cr],0,k); for(;pre[cr];cr=pre[cr]) { dis=pd(x,pre[cr]); mdfy(0,n,rt[pre[cr]],dis,k); mdfy(0,n,rt[cr+n],dis,k); }}int main(){ n=rdn(); int Q=rdn(); for(int i=1;i<=n;i++)w[i]=rdn(); for(int i=1,u,v;i
最后写了 vector 实现的树状数组。 v . resize( k ) 可以给 v 开出 k 大小,角标是 0 到 k-1 。
人家还存下来了每个点的 log 个点分树上祖先以及自己到它们的距离,感觉好清爽。(速度也变快了?)
树状数组查不到 0 位置的值,所以那里手动加上 w[cr] (就和自己第一个版本一样)。
#include#include #include #include using namespace std;const int N=1e5+5,K=20;int n,w[N],hd[N],xnt,to[N<<1],nxt[N<<1],ans,mn,rt,siz[N];int dep[N],pre[N][K],fsz[N],gsz[N],dis[N][K]; vector f[N],g[N];bool vis[N];int rdn(){ int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret;}int dg[20];void wrt(int x){ if(!x){puts("0");return;} int tt=0;while(x)dg[++tt]=x%10,x/=10; for(;tt;tt--)putchar(dg[tt]+'0');puts("");}int Mx(int a,int b){ return a>b?a:b;}int Mn(int a,int b){ return a yd)continue; ans+=qry(pre[cr][i],yd-dis[cr][i]); ans-=qryx(pre[cr][i+1],yd-dis[cr][i]); }}void mdfy(int cr,int k){ for(int i=dep[cr]-1;i;i--) { add(pre[cr][i],dis[cr][i],k); addx(pre[cr][i+1],dis[cr][i],k); }}int main(){ n=rdn(); int Q=rdn(); for(int i=1;i<=n;i++)w[i]=rdn(); for(int i=1,u,v;i