bzoj1103 POI2007 大都市meg

看了下noi的水题 然后第一次自己YY出dfs序这个东西(当然以前听别人讲过没写过)

然后做了一道dfs序的水题

做法:

统计出一个点的dfs序,成为pos[i]和以该节点为子树的所有点中pos[i]的最大值en[i]

然后用树状数组单点修改区间询问处理

可以把询问的过程想象为一次dfs,走到一条边ans++,返回ans--,那么对于一个点我们add(pos[x],1) add(en[x]+1,-1)来模拟这个过程 也就是去掉分叉的影响,不妨加一个超级源连在1号点,那么每次都是通过一条边来到了这里,所以query(pos[i])就是通过的边数,然而超级源的边不能计算,所以ans--。

对于修改,一条边会使他的子树里的所有点的答案都-1,所以add(pos[x],-1) add(en[x]+1,1)

然而bzoj上不用手写栈

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>

using namespace std;

const int Maxn=250000+10;

int pos[Maxn],en[Maxn],clk=0;

struct Edge{
    int to;
    Edge*next;
    Edge(int to=0,Edge*next=0):to(to),next(next) {}
}pool[Maxn*2],*fir[Maxn],*pis=pool;
void AddEdge(int from,int to) {
    *pis=Edge(to,fir[from]);fir[from]=pis++;
    *pis=Edge(from,fir[to]);fir[to]=pis++;
}
int fa[Maxn];
void dfs(int x) {
    pos[x]=++clk;
    for(Edge*p=fir[x];p;p=p->next) {
        int v=p->to;
        if(v==fa[x]) continue;
        fa[v]=x;
        dfs(v);
    }
    en[x]=clk;
}    

int C[Maxn],n;
void add(int x,int d) {
    for(;0<x&&x<=n;x+=x&-x) C[x]+=d;
}

int query(int x) {
    int ret=0;
    for(;0<x&&x<=n;x-=x&-x) ret+=C[x];
    return ret;
}

int main() {
#ifdef DEBUG
    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
#endif

    scanf("%d",&n);
    for(int i=1;i<n;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        AddEdge(u,v);
    }
    dfs(1); 
    for(int i=1;i<=n;i++) {
        add(pos[i],1);
        add(en[i]+1,-1);
    }
    int m;
    for(scanf("%d",&m);m;) {
        char opt;
        int x,y;
        while(opt=getchar(),opt!='A'&&opt!='W');
        if(opt=='A') {
            scanf("%d%d",&x,&y);
            if(fa[y]==x) swap(x,y);
            add(pos[x],-1);
            add(en[x]+1,1);
        }else {
            scanf("%d",&x);
            printf("%d
",query(pos[x])-1);
            m--;
        }
    }
    
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/showson/p/4655529.html