Libre OJ 144、145 (DFS序)

部分参考自博客:https://blog.csdn.net/hpu2022/article/details/81910490

在许多问题中,由于树结构复杂通常会导致问题很棘手,因为其实非线性结构,操作起来也甚是费时。

例如:对于一棵树,含有n个节点,每个节点拥有相应的权值,我们进行很多个操作,比如可以修改某个节点的权值,查找以某个节点为根节点的子树和。

显然,对于这个问题,每次计算子树权值和时我们都要遍历一下各个节点,而如果我们可以用某种方式把它装化成线性结构,然后再用数组数组或者线段树去更新查询,这样不就可以更高效得多吗?

 

没错,这就有了我们DFS序,它的主要思路就是将树形结构转化成线性结构,用dfs遍历一遍这棵树,进入到x节点有一个in时间戳,递归退出时有一个out 时间戳,x节点的两个时间戳之间遍历到的点,就是根为x的子树的所有节点,他们的dfs进入时间戳是递增的。同时两个时间戳构成了一个区间,x节点在这段区间的最左端,这个区间就是一棵根节点为x的子树,对于区间的操作就是其他维护方式的应用了。

 

int time = 0;
inline void dfs(int x, int fa) {
    in[x] = ++time; //进入的时间戳
    num[time] = x;  //生成新的线性结构
    for(int i = 0; i < G[x].size(); i++) {
        int cnt = G[x][i];
        if(cnt == fa) continue;
        dfs(cnt, x);
    }
    out[x] = time;  //出去的时间戳
}

in[x]表示映射的DFS预处理出的线性结构,也就是说x是原始节点,in[x]是x节点的新位置,num[t]表示第t个节点的编号,num[in[x]]表示的还是x。num是新序列,in表示是新序列的下标,in[x]~out[x]是x为根结点的子树,划分为一个区间。 

 

Loj144 DFS序+树状数组单点更新区间查找

题目链接:https://loj.ac/problem/144

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
const ll maxn=1e6+10;
int n,q,m,r,tot,cnt;
ll v[maxn],in[maxn],out[maxn],head[maxn],x,num[maxn],sum[maxn];
struct node{
    int to,next;
}edge[2*maxn];
void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int pos,int fa){
    num[++cnt]=pos;
    in[pos]=cnt;
    for(int i=head[pos];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v!=fa) dfs(v,pos);
    }
    out[pos]=cnt;
}
int lowbit(int x){
    return x&(-x);
}
void update(int x,int y){
    while(x<=n){
        sum[x]+=y;
        x+=lowbit(x);
    }
}
ll ask(int x){
    ll res=0;
    while(x){
        res+=sum[x];
        x-=lowbit(x);
    }
    return res;
}
int main(){
    scanf("%d%d%d",&n,&m,&r);
    for(int i=1;i<=n;i++)
        scanf("%lld",&v[i]),head[i]=-1;
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v); add(v,u);
    }
    dfs(r,-1);
    for(int i=1;i<=n;i++)
        update(in[i],v[i]);
    while(m--){
        int op,x,y;
        scanf("%d",&op);
        if(op==1){
            scanf("%d%d",&x,&y);
            update(in[x],y);
        }
        else{
            scanf("%d",&x);
            printf("%lld\n",ask(out[x])-ask(in[x]-1));
        }
    }
    return 0;
}

 

Loj 145 DFS序+树状数组区间更新区间查找

题目链接:https://loj.ac/problem/145

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
const ll maxn=1e6+10;
int n,q,m,r,tot,cnt;
ll v[maxn],in[maxn],out[maxn],head[maxn],x,num[maxn],sum1[maxn],sum2[maxn];
struct node{
    int to,next;
}edge[2*maxn];
void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int pos,int fa){
    num[++cnt]=pos;
    in[pos]=cnt;
    for(int i=head[pos];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v!=fa) dfs(v,pos);
    }
    out[pos]=cnt;
}
int lowbit(int x){return x&(-x);}
void update(int x,ll y){
    for(int i=x;i<=n;i+=lowbit(i)){
        sum1[i]+=y;
        sum2[i]+=(x-1)*y;
    }
}
ll ask(int x){
    ll res=0;
    for(int i=x;i;i-=lowbit(i)){
        res+=x*sum1[i]-sum2[i];
    }
    return res;
}
int main(){
    scanf("%d%d%d",&n,&m,&r);
    for(int i=1;i<=n;i++)
        scanf("%lld",&v[i]),head[i]=-1;
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v); add(v,u);
    }
    dfs(r,-1);
    for(int i=1;i<=n;i++)
        update(in[i],v[i]-v[num[in[i]-1]]);
    while(m--){
        int op,x,y;
        scanf("%d",&op);
        if(op==1){
            scanf("%d%d",&x,&y);
            update(in[x],y); update(out[x]+1,-y);
        }
        else{
            scanf("%d",&x);
            printf("%lld\n",ask(out[x])-ask(in[x]-1));
        }
    }
    return 0;
}

 

posted @ 2019-03-15 23:21  两点够吗  阅读(564)  评论(0编辑  收藏  举报