树链剖分-SPOJ375(QTREE)
QTREE - Query on a tree
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1.
We will ask you to perfrom some instructions of the following form:
- CHANGE i ti : change the cost of the i-th edge to ti
or - QUERY a b : ask for the maximum edge cost on the path from node a to node b
Input
The first line of input contains an integer t, the number of test cases (t <= 20). t test cases follow.
For each test case:
- In the first line there is an integer N (N <= 10000),
- In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of cost c (c <= 1000000),
- The next lines contain instructions "CHANGE i ti" or "QUERY a b",
- The end of each test case is signified by the string "DONE".
There is one blank line between successive tests.
Output
For each "QUERY" operation, write one integer representing its result.
Example
Input: 1 3 1 2 1 2 3 2 QUERY 1 2 CHANGE 1 3 QUERY 1 2 DONE Output: 1 3
传送门
一句话题意:给出一棵树,要求在树上:
1)更新某个节点的值
2)求两个节点间最短路径上的最大值
模板题,数据结构解析详见代码。
1 #include<cstring> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #define clr(a) memset(a,0,sizeof(a)); 6 #define foru(i,x,y) for(int i=x;i<=y;i++) 7 using namespace std; 8 const int N=10010; 9 struct edge{int to,nxt;}e[N*2]; 10 int t[N],a[N][3],d[N],id[N],head[N],f[N],siz[N],son[N],top[N]; 11 //f[v] 节点v的父节点编号 12 //id[v] 节点v的父边在线段树中的编号 13 //siz[v] 以节点v为根的数中的节点数 14 //son[v] 节点v的子节点中siz[]最大的节点编号 15 //top[v] 节点v所在重链的顶端节点编号 16 //d[v] 节点v的深度 17 int ne,cnt,n; 18 19 void add(int a,int b){ 20 e[++ne]=(edge){b,head[a]};head[a]=ne; 21 } 22 23 /////////////////////////////树剖//////////////////////////////////// 24 25 void dfs(int k,int fa,int dep){//统计f[] siz[] son[] d[] 26 f[k]=fa;d[k]=dep;siz[k]=1;son[k]=0; 27 for(int i=head[k];i;i=e[i].nxt){ 28 int v=e[i].to; 29 if(v==fa)continue;//无向图判是否走过,易漏 30 dfs(v,k,dep+1); 31 siz[k]+=siz[v]; 32 if(siz[v]>siz[son[k]])son[k]=v; 33 } 34 } 35 36 void build(int k,int tp){ 37 id[k]=++cnt;//按序将边加入线段树 38 top[k]=tp; 39 if(son[k])build(son[k],tp);//重儿子的top[]从重链顶端继承 40 for(int i=head[k];i;i=e[i].nxt) 41 if(e[i].to!=son[k]&&e[i].to!=f[k]) 42 build(e[i].to,e[i].to);//轻儿子top[]为自身 43 } 44 /////////////////////////////树剖//////////////////////////////////// 45 46 47 /////////////////////////////线段树//////////////////////////////////// 48 #define mid ((L+R)>>1) 49 #define ls (k<<1)//写位运算一定要开-Wall,否则一定要记得加括号 50 #define rs ls+1 51 52 void update(int k,int L,int R,int p,int x){ 53 if(p>R||p<L)return; 54 if(L==R){t[k]=x;return;} 55 update(ls,L,mid,p,x); update(rs,mid+1,R,p,x); 56 t[k]=max(t[ls],t[rs]); 57 } 58 59 int query(int k,int L,int R,int l,int r){ 60 if(l>R||r<L)return 0; 61 if(l<=L&&R<=r)return t[k]; 62 return max(query(ls,L,mid,l,r),query(rs,mid+1,R,l,r)); 63 } 64 /////////////////////////////线段树//////////////////////////////////// 65 66 int find(int x,int y){ 67 int ans=-1e9; 68 while(top[x]!=top[y]){//类似LCA,每次将较低的节点上跳,并统计路径上的最大值 69 if(d[top[x]]<d[top[y]])swap(x,y); 70 ans=max(ans,query(1,1,cnt,id[top[x]],id[x])); 71 x=f[top[x]]; 72 } 73 if(d[x]>d[y])swap(x,y);//当两点处于同一条链上的时候,进行最后一次统计 74 if(x!=y)ans=max(ans,query(1,1,cnt,id[x]+1,id[y]));//注意,因为id[x]为x的父边,所以若不+1,就会多统计一条边 75 return ans; 76 } 77 78 char ch[16]; 79 void work(){ 80 int x,y; 81 while(scanf("%s",ch),ch[0]!='D'){ 82 scanf("%d%d",&x,&y); 83 if(ch[0]=='C')update(1,1,cnt,id[a[x][1]],y); 84 else printf("%d\n",find(x,y)); 85 } 86 } 87 88 void init(){ 89 clr(a);clr(e);clr(t);clr(son);clr(siz);clr(id);clr(top);clr(head);clr(d);clr(f); 90 ne=cnt=n=0; 91 scanf("%d",&n); 92 foru(i,1,n-1){ 93 scanf("%d%d%d",&a[i][0],&a[i][1],&a[i][2]); 94 add(a[i][0],a[i][1]);add(a[i][1],a[i][0]); 95 } 96 dfs(1,0,1); 97 build(1,1); 98 foru(i,1,n-1){ 99 if(d[a[i][0]]>d[a[i][1]])swap(a[i][0],a[i][1]); 100 update(1,1,cnt,id[a[i][1]],a[i][2]);//建树 101 } 102 } 103 104 int main(){ 105 int T; 106 scanf("%d",&T); 107 while(T--){ 108 init(); 109 work(); 110 } 111 }