bzoj1103: [POI2007]大都市meg

dfs序。

用l[u]和r[u]表示进入u和出去u的时间。用树状数组维护前缀和,l[u]处加1,r[u]处减1。

询问的是树根到自己u的距离,就相当于l[u]处的前缀和。为什么呢?

如果一个节点v在树根到u的路径上,就会在l[v]处加1。如果不在,如果编号小于u,l[v]和r[v]处相消。

如果大于u则l[v]大于l[u]。W u操作则是将l[u]处减1,将r[u]处加1。这样u就不会影响路径长度了。

dfs序的巧妙应用。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 250000 + 10;
const int maxm = 500000 + 10;

struct BIT {
    int a[maxn<<1],n;
    
    inline int lowbit(int x) {
        return (x&(-x));
    }
    
    void add(int x,int v) {
        for(;x<=n;x+=lowbit(x))
            a[x]+=v;    
    }
    
    int sum(int x) {
        int res=0;
        for(;x;x-=lowbit(x)) res+=a[x];
        return res;
    }
    
    void init(int m) {
        memset(a,0,sizeof(a));
        n=m;
    }
    
} bit;

int g[maxn],v[maxm],next[maxm],eid;
int fa[maxn],l[maxn],r[maxn],s[maxn],sp,dfn;
int n,m;


void addedge(int a,int b) {
    v[eid]=b; next[eid]=g[a]; g[a]=eid++;
    v[eid]=a; next[eid]=g[b]; g[b]=eid++;
}

void dfs() {
    s[++sp]=1;
    
    while(sp) {
        int u=s[sp--];
        if(!l[u]) {
            l[u]=++dfn;
            s[++sp]=u;
            for(int i=g[u];~i;i=next[i]) if(v[i]!=fa[u]) {
                fa[v[i]]=u; 
                s[++sp]=v[i];
            }
        }
        else r[u]=++dfn;
    }
}

int main() {
    scanf("%d",&n);
    memset(g,-1,sizeof(g));
    bit.init(n*2);
    for(int i=1,a,b;i<n;i++) {
        scanf("%d%d",&a,&b);
        addedge(a,b);
    }
    dfs();
    
    for(int i=2;i<=n;i++) {
        bit.add(l[i],1);
        bit.add(r[i],-1);    
    }
    char op[10];
    scanf("%d",&m); m+=(n-1);
    for(int    i=1,a,b;i<=m;i++) {
        scanf("%s%d",s,&a);
        if(s[0]=='A') {
            scanf("%d",&b);
            if(a>b) swap(a,b);
            bit.add(l[b],-1);
            bit.add(r[b],1);    
        }
        else printf("%d\n",bit.sum(l[a]));
    }
    return 0;    
}
posted @ 2016-05-10 17:56  invoid  阅读(138)  评论(0编辑  收藏  举报