DFS序的学习

来源:互联网 发布:魔侠传 网络异常 编辑:程序博客网 时间:2024/06/03 18:45

//DFS序是个很有用的东西, 可以将树上的信息维护成一个连续的区间, 从而使树变成一条一条的链, 我们就可以用线段树来维护这些链, 从而求出我们所要的东西. 因为遍历的次序. 一个结点和其子树一定是在一个连续的区间内. 遍历的写法:

void dfs_id(int u,int fa){    p1[u] = ++ti;    for(int i=head[u]; ~i ; i = s[i].next){        int to = s[i].to;        if(fa == to) continue;        dfs_id(to,u);    }    p2[u] = ti;} //p1存的是第一次遇到时序号,p2是第二次遇到的序号.//那么结点u及其子树存在的连续区间就是p1[u] - p2[u].

那么具体看几个简单例题

HDU - 3887 传送门
//题意: 问你对于每个节点,它的子树上标号比它小的点有多少个
//思路: 子树的问题,dfs序可以很轻松的解决,因为点在它的子树上,所以在线段树中,必定在它的两个时间戳的区间之间,所以我们只需要从小到大考虑,它的区间里有多少个点已经放了,然后再把它放进去. 很容易的解决了 . DFS 序的入门题.
AC Code

/** @Cain*/const int maxn = 1e5+5;int cas=1;int cnt,head[maxn];int n,root,ti;int p1[maxn],p2[maxn]; //记录树上的每个点遍历时它的次序.struct node{    int to,next;}s[maxn*2];void add(int u,int v){    s[cnt].to = v;    s[cnt].next = head[u];    head[u] = cnt++;}void dfs_id(int u,int fa){    p1[u] = ++ti;    for(int i=head[u]; ~i ; i = s[i].next){        int to = s[i].to;        if(fa == to) continue;        dfs_id(to,u);    }    p2[u] = ti;}int sum[maxn*4];void pushup(int rt){    sum[rt] = sum[rt<<1] + sum[rt<<1|1];}void build(int l,int r,int rt){    sum[rt] = 0;    if(l == r) return;    int mid = (l+r)>>1;    build(l,mid,rt<<1);    build(mid+1,r,rt<<1|1);}void update(int l,int r,int rt,int pos){    if(l == r){        sum[rt]++;        return ;    }    int mid = (l+r)>>1;    if(pos<=mid) update(l,mid,rt<<1,pos);    else update(mid+1,r,rt<<1|1,pos);    pushup(rt);}int query(int l,int r,int rt,int ql,int qr){    if(ql<=l && r<=qr) return sum[rt];    int mid = (l+r) >> 1;    int res = 0;    if(ql <= mid) res += query(l,mid,rt<<1,ql,qr);    if(qr > mid) res += query(mid+1,r,rt<<1|1,ql,qr);    return res;}void solve(){    while(~scanf("%d%d",&n,&root)){        if(n + root == 0) break;        Fill(head,-1); cnt = ti = 0;        for(int i=1;i<=n-1;i++){            int u,v; scanf("%d%d",&u,&v);            add(u,v); add(v,u);        }        dfs_id(root,-1);        /*for(int i=1;i<=n;i++){            printf("%d %d \n",p1[i],p2[i]);        }*/        build(1,n,1);        for(int i=1;i<=n;i++){            printf("%d%c",query(1,n,1,p1[i],p2[i]),i==n?'\n':' ');            update(1,n,1,p1[i]);        }    }}

poj - 3321 传送门
//题意: 一开始告诉你树上每个节点都有1个苹果,然后你对一个节点操作,如果有苹果,就拿走,没苹果,就放上,然后询问你以x为根的子树上有多少个苹果.
//思路: 有上面那道题的基础, 我相信这道题是非常简单的吧. 那么直接做呗. DFS序水题.
AC Code

/** @Cain*/const int maxn = 1e5+5;int cas=1;int cnt,head[maxn];int n,ti;int p1[maxn],p2[maxn]; //记录树上的每个点遍历时它的次序.struct node{    int to,next;}s[maxn*2];void add(int u,int v){    s[cnt].to = v;    s[cnt].next = head[u];    head[u] = cnt++;}void dfs_id(int u,int fa){    p1[u] = ++ti;    for(int i=head[u]; ~i ; i = s[i].next){        int to = s[i].to;        if(fa == to) continue;        dfs_id(to,u);    }    p2[u] = ti;}int sum[maxn*4];void pushup(int rt){    sum[rt] = sum[rt<<1] + sum[rt<<1|1];}void build(int l,int r,int rt){    if(l == r) {        sum[rt] = 1;        return;    }    int mid = (l+r)>>1;    build(l,mid,rt<<1);    build(mid+1,r,rt<<1|1);    pushup(rt);}void update(int l,int r,int rt,int pos){    if(l == r){        if(sum[rt]) sum[rt] = 0;        else sum[rt] = 1;        return ;    }    int mid = (l+r)>>1;    if(pos<=mid) update(l,mid,rt<<1,pos);    else update(mid+1,r,rt<<1|1,pos);    pushup(rt);}int query(int l,int r,int rt,int ql,int qr){    if(ql<=l && r<=qr) return sum[rt];    int mid = (l+r) >> 1;    int res = 0;    if(ql <= mid) res += query(l,mid,rt<<1,ql,qr);    if(qr > mid) res += query(mid+1,r,rt<<1|1,ql,qr);    return res;}void solve(){    while(~scanf("%d",&n)){        Fill(head,-1); cnt = ti = 0;        for(int i=1;i<=n-1;i++){            int u,v; scanf("%d%d",&u,&v);            add(u,v); add(v,u);        }        dfs_id(1,-1);        /*for(int i=1;i<=n;i++){            printf("%d %d \n",p1[i],p2[i]);        }*/        int m; scanf("%d",&m);        build(1,n,1);        while(m--){            char s[10]; scanf("%s",s);            if(s[0] == 'Q') {                int u; scanf("%d",&u);                printf("%d\n",query(1,n,1,p1[u],p2[u]));            }            else {                int u; scanf("%d",&u);                update(1,n,1,p1[u]);            }        }    }}
原创粉丝点击