【BZOJ】3730: 震波 -点分树&线段树

题解

此题最好用BIT,线段树TLE了..
我们先构建点分树, nlog2n n l o g 2 n 把每个重心所管辖子树内每个点以距离为下标,点权为值加入到动态开点线段树里,注意这里要分别建两棵线段树,一棵处理重心,一棵处理重心到点分树上的父节点处理的容斥信息。
修改和求和都可以 nlog2n n l o g 2 n 处理。
这题最坑的是:暴力跳点分树的时候不会中途弹出,有可能其中有一层的父节点离x很远,下一个又很近。要是中途弹出会RE(WA)到死TAT
还有就是空间往大了开…


代码

#include
using namespace std;
const int N=500100;
const int M=N<<4;
int n,m,val[N],f[N],vis[N],d[N];
int F[N][23],D[N],bin[23];
int S,MX,sz[N],mx[N],root,ans;
int head[N],to[N<<1],nxt[N<<1],tot;
int ss[M],cnt;
int rt[N][2],ls[M],rs[M];

inline int rd()
{
    char ch=getchar();int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline void dfs(int x)
{
    int i,j;
    for(i=1;bin[i]<=D[x];++i)
     F[x][i]=F[F[x][i-1]][i-1];
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(j==F[x][0]) continue;
        F[j][0]=x;D[j]=D[x]+1;
        dfs(j);
    }
}

inline int LCA(int x,int y)
{
    if(D[x]int i,j,t=D[x]-D[y];
    for(i=0;bin[i]<=t;++i)
     if(bin[i]&t) x=F[x][i];
    if(x==y) return x;
    for(i=18;i>=0 && x!=y;--i)
     if(F[x][i]!=F[y][i])
      x=F[x][i],y=F[y][i];
    return x==y?x:F[x][0];
}

inline int getdis(int x,int y)
{return D[x]+D[y]-2*D[LCA(x,y)];}

inline void getrt(int x,int fa)
{
    int i,j;
    sz[x]=1;mx[x]=0;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j] || j==fa) continue;
        getrt(j,x);sz[x]+=sz[j];
        mx[x]=max(mx[x],sz[j]);
    }
    mx[x]=max(mx[x],S-sz[x]);
    if(mx[x]inline void getsz(int x,int fa)
{
    sz[x]=1;
    for(int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j] || j==fa) continue;
        getsz(j,x);sz[x]+=sz[j];
    }
}

inline void ad(int &k,int l,int r,int pos,int vv)
{
    if(!k) {cnt++;k=cnt;}
    ss[k]+=vv;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) ad(ls[k],l,mid,pos,vv);
    else ad(rs[k],mid+1,r,pos,vv);
}

inline int get(int k,int l,int r,int pos)
{
    if(!k) return 0;
    if(r<=pos) return ss[k];
    int re=0,mid=(l+r)>>1;
    re+=get(ls[k],l,mid,pos);
    if(pos>mid) re+=get(rs[k],mid+1,r,pos);
    return re;
}

inline void update(int u,int l,int x,int fa)
{
    ad(rt[u][l],0,n,d[x],val[x]);
    for(int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j] || j==fa) continue;
        d[j]=d[x]+1;update(u,l,j,x);
    }
}

inline void build(int x)
{
    int i,j;
    vis[x]=1;d[x]=0;update(x,0,x,0);
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j]) continue;
        getsz(j,x);S=sz[j];MX=n+1;
        getrt(j,x);f[root]=x;
        d[j]=1;update(root,1,j,x);
        build(root);
    }
}

int main(){
    int i,j,op,ix,iy,dd;
    bin[0]=1;for(i=1;i<21;++i) bin[i]=bin[i-1]<<1;
    n=rd();m=rd();
    for(i=1;i<=n;++i) val[i]=rd();
    for(i=1;i1);
    S=n;MX=n+1;getrt(1,0);
    build(root);
    while(m--){
        op=rd();
        ix=rd()^ans;iy=rd()^ans;
        if(!op){
           ans=0;dd=0;
           for(i=ix;i;i=f[i]){
             if(iy>=dd) ans+=get(rt[i][0],0,n,iy-dd);
             if(!f[i]) break;
             dd=getdis(f[i],ix);
             if(iy>=dd) ans-=get(rt[i][1],0,n,iy-dd);
           }
           printf("%d\n",ans);
        }else{
            j=iy-val[ix];dd=0;
            for(i=ix;i;i=f[i]){
                ad(rt[i][0],0,n,dd,j);
                if(!f[i]) break;
                dd=getdis(f[i],ix);
                ad(rt[i][1],0,n,dd,j);
            }
            val[ix]=iy;
        }
    }
}

你可能感兴趣的:(线段树,点分树)