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; }