[BZOJ4196][NOI2005]软件包管理器(链剖+dfs序)

题目描述

传送门

题解

链剖练习题,思路很好想。
刚开始的时候出了个小bug,让我现在十分怀疑自己写数据结构的一次成功率。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=1e5+5;
const int max_e=max_n*2;
const int max_tree=max_n*5;

int n,q,x,N,ans;
char s[20];
int tot,point[max_n],next[max_e],v[max_e];
int size[max_n],h[max_n],father[max_n],son[max_n];
int top[max_n],in[max_n],out[max_n];
int sum[max_tree],delta[max_tree];

inline void addedge(int x,int y){
    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}

inline void dfs_1(int x,int fa,int dep){
    size[x]=1; h[x]=dep; father[x]=fa;
    int maxson=0;

    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa){
        dfs_1(v[i],x,dep+1);
        size[x]+=size[v[i]];
        if (maxson<size[v[i]]){
            maxson=size[v[i]];
            son[x]=v[i];
        }
      }
}

inline void dfs_2(int x,int fa){
    if (son[fa]!=x) top[x]=x;
    else top[x]=top[fa];
    in[x]=++N;

    if (son[x]) dfs_2(son[x],x);
    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa&&v[i]!=son[x])
        dfs_2(v[i],x);

    out[x]=N;
}

inline void update(int now){
    sum[now]=sum[now<<1]+sum[now<<1|1];
}

inline void pushdown(int now,int l,int r,int mid){
    if (delta[now]>=0){
        sum[now<<1]=delta[now]*(mid-l+1);
        delta[now<<1]=delta[now];
        sum[now<<1|1]=delta[now]*(r-mid);
        delta[now<<1|1]=delta[now];
        delta[now]=-1;
    }
}

inline void interval_change(int now,int l,int r,int lrange,int rrange,int v){
    int mid=(l+r)>>1;
    if (lrange<=l&&r<=rrange){
        sum[now]=(r-l+1)*v;
        delta[now]=v;
        return;
    }

    pushdown(now,l,r,mid);
    if (lrange<=mid)
      interval_change(now<<1,l,mid,lrange,rrange,v);
    if (mid+1<=rrange)
      interval_change(now<<1|1,mid+1,r,lrange,rrange,v);
    update(now);
}

inline int query(int now,int l,int r,int lrange,int rrange){
    int mid=(l+r)>>1,ans=0;
    if (lrange<=l&&r<=rrange) return sum[now];

    pushdown(now,l,r,mid);
    if (lrange<=mid)
      ans+=query(now<<1,l,mid,lrange,rrange);
    if (mid+1<=rrange)
      ans+=query(now<<1|1,mid+1,r,lrange,rrange);
    return ans;
}

inline void QUERY(int u,int t){
    int f1=top[u],f2=top[t],ans=0;
    while (f1!=f2){
        if (h[f1]<h[f2]){
            swap(f1,f2);
            swap(u,t);
        }
        ans+=query(1,1,N,in[f1],in[u]);
        u=father[f1];
        f1=top[u];
    }
    if (in[u]>in[t]) swap(u,t);
    ans+=query(1,1,N,in[u],in[t]);
    int cnt=h[x];
    printf("%d\n",cnt-ans);
}

inline void CHANGE(int u,int t){
    int f1=top[u],f2=top[t],ans=0;
    while (f1!=f2){
        if (h[f1]<h[f2]){
            swap(f1,f2);
            swap(u,t);
        }
        interval_change(1,1,N,in[f1],in[u],1);
        u=father[f1];
        f1=top[u];
    }
    if (in[u]>in[t]) swap(u,t);
    interval_change(1,1,N,in[u],in[t],1);
}

inline void INSTALL(int u,int t){
    QUERY(u,t);
    CHANGE(u,t);
}

inline void UNINSTALL(int x){
    ans=query(1,1,N,in[x],out[x]);
    printf("%d\n",ans);

    interval_change(1,1,N,in[x],out[x],0);
}

int main(){
// freopen("input.txt","r",stdin);
    scanf("%d",&n);
    for (int i=1;i<n;++i){
        scanf("%d",&x);
        ++x;
        addedge(x,i+1);
    }

    dfs_1(1,0,1);
    dfs_2(1,0);

    //0未安装 1已安装 
    memset(sum,0,sizeof(sum));
    memset(delta,128,sizeof(delta));

    scanf("%d",&q);
    for (int i=1;i<=q;++i){
        scanf("%s",s);
        scanf("%d",&x);
        ++x;
        if (s[0]=='i')
          INSTALL(1,x);
        else
          UNINSTALL(x);
    }
}

总结

注意线段树中的初始状态(尤其delta)不能与线段树中要出现的状态重合(像本题中的0)。线段树是要直接修改还是要加减要注意。

你可能感兴趣的:(dfs序,bzoj,链剖,NOI2005)