DFS序与欧拉序是把图论和数据结构进行紧密结合的一个典范
非常喜欢这两个东西
为什么先介绍欧拉序呢?因为DFS序的那道题目,题干比较长。。
从根结点出发,按dfs的顺序在绕回原点所经过所有点的顺序
假如你会跑树的DFS,那么这个过程就一定不会陌生
欧拉序求LCA我感觉和Tarjan算法求LCA有联系,可能是我还没有理解Tarjan才会这么去说
然后欧拉序的一个神奇的作用是修改子树和查询子树
欧拉序+Splay组成的那种树叫做欧拉游览树,它是一种动态树的实现(能够和LCT分庭抗礼)
如果静态树的话就不用Splay来维护了
直接干
切完这道题就回家,明天把DFS序+主席树神题做了
给你一棵树,告诉你每个点的点权,给你m个x和w,表示将子树x中每个点的权值和加w,然后再给你t个x,表示询问x子树中所有点的权值之和
欧拉序:每个点都出现了两遍,而这个点第一次出现和第二次出现的位置之间就是他的所有子孙节点
这样可以方便提取子树,让后放在维护区间的数据结构上去做就好了
这个区间用差分数组来维护貌似就可以修改区间查询点了
我们可以直接通过该点第二次出现的位置+1来获得第一个不是该子树的点
对于查询操作,同时维护一个前缀和就好了
???
不过由于每个子树中的点都被算了两遍,我们要除以二
这份代码应该是对的,应该是对的,应该是对的
1 #include<cstdio> 2 const int maxn=100005; 3 const int maxm=200005; 4 int n,m,t,cnt,len; 5 int a[maxn],g[maxn],euler[maxm],sub[maxm]; 6 int pos[maxn][2]; 7 struct Edge 8 { 9 int t,next; 10 }e[maxm]; 11 void insert(int u,int v) 12 { 13 cnt++;e[cnt].t=v;e[cnt].next=g[u];g[u]=cnt; 14 cnt++;e[cnt].t=u;e[cnt].next=g[v];g[v]=cnt; 15 } 16 void dfs(int u,int fa) 17 { 18 euler[++len]=u; 19 pos[u][0]=len; 20 for(int tmp=g[u];tmp;tmp=e[tmp].next) 21 if(e[tmp].t!=fa) dfs(e[tmp].t,u); 22 euler[++len]=u; 23 pos[u][1]=len; 24 } 25 int main() 26 { 27 scanf("%d%d%d",&n,&m,&t); 28 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 29 for(int i=1;i<n;i++) 30 { 31 int u,v; 32 scanf("%d%d",&u,&v); 33 insert(u,v); 34 } 35 dfs(1,0); 36 sub[1]=a[euler[1]]; 37 for(int i=2;i<=len;i++) 38 sub[i]=a[euler[i]]-a[euler[i-1]]; 39 for(int i=1;i<=m;i++) //修改子树 40 { 41 int x,w; 42 scanf("%d%d",&x,&w); 43 sub[pos[x][0]]+=w; 44 sub[pos[x][1]+1]-=w; 45 } 46 for(int i=1;i<=len;i++) 47 sub[i]=sub[i-1]+sub[i]; 48 for(int i=1;i<=len;i++) //如果删去维护前缀和的过程,就是查询单个点的权值 49 sub[i]=sub[i-1]+sub[i]; 50 for(int i=1;i<=t;i++) 51 { 52 int x; 53 scanf("%d",&x); 54 printf("%d\n",(sub[pos[x][1]]-sub[pos[x][0]-1])/2); 55 } 56 return 0; 57 }