标签:
给你一棵带权树,边权表示两点间的距离。有如下两种操作:
对每个操作1,回答两点间的距离。
第一行一个数n,表示点的个数。接下来n−1行,每行3个数u,v,w,表示u,v间有权为w的边。边按照输入顺序从1到n−1编号。输入保证为一颗树。 (2≤n≤105,1≤u,v≤n,1≤w≤1000)
之后输入一个数q,表示询问个数。最后有q行,每行第一个数op(op=1或2)为操作类型。
当op=1时,输入u,v表示询问u,v间的距离。 (u≠v,1≤u,v≤n)
当op为2时,输入id,w表示把编号为id的边的权值改为w。 (1≤q≤105,1≤id≤n−1,1≤w≤1000)
对于对每个操作1,输出一行,回答两点间的距离。
Sample Input | Sample Output |
---|---|
5 1 2 2 2 3 3 1 5 1 2 4 4 5 1 1 3 1 2 5 2 1 1 2 3 3 1 2 5 |
5 3 4 |
解题报告:
这是一道 LCA + 欧拉序列 + 线段树题目
首先我们容易得出下列这个式子
设 distance(x) 为树上的点 x 到根的距离,设u为x,y的最近公共祖先
则distance of (x – y) = distance(x) + distance(y) – 2*distance(u)
那么我们该如何求得LCA呢
这里我采用的是tarjan离线处理所有询问.
一遍dfs处理所有询问的LCA,复杂度为O(n+q)
之后我们用一次dfs将树转为线性结构.
即记录这个点的入时间戳,也记录这个点的出时间戳.
容易得到[ 入时间戳 + 1 , 出时间戳 ] 恰好包含了这个点及其儿子
这样,本题就转换成了线段树区间更新 + 点查询的题目
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> #define pb push_back using namespace std; const int maxn = 2e5 + 20; typedef struct Edge { int u,v,w; Edge(int u,int v,int w) { this->u = u , this->v = v , this->w = w ; } }; typedef struct treenode { int l,r,lazy,sum; void updata(int x) { lazy += x; sum += (r-l+1)*x; } }; typedef struct querynode { int type,t1,t2,lca; }; typedef struct tarjan { int v , ansid; tarjan(int v,int ansid) { this->v = v ,this->ansid = ansid; } }; treenode tree[maxn*8]; void build_tree(int o,int l,int r) { tree[o].l = l , tree[o].r = r ; tree[o].sum = tree[o].lazy = 0; if (r > l) { int mid = l + (r-l)/2; build_tree(2*o,l,mid); build_tree(2*o+1,mid+1,r); } } inline void push_up(int x) { tree[x].sum = tree[2*x].sum + tree[2*x+1].sum; } inline void push_down(int x) { int lazy = tree[x].lazy; if (lazy) tree[2*x].updata(lazy),tree[2*x+1].updata(lazy); tree[x].lazy = 0; } void updata(int ql,int qr,int o,int v) { int l = tree[o].l , r = tree[o].r; if (ql <= l && qr >= r ) tree[o].updata(v); else { int mid = l + (r-l)/2; push_down(o); if (mid >= ql) updata(ql,qr,2*o,v); if (mid < qr) updata(ql,qr,2*o+1,v); push_up(o); } } int querysum(int ql,int qr,int o) { int l = tree[o].l , r = tree[o].r; if (ql <= l && qr >= r) return tree[o].sum; else { int mid = l + (r-l)/2; push_down(o); int res = 0; if (mid >= ql) res += querysum(ql,qr,2*o); if (mid < qr) res += querysum(ql,qr,2*o+1); push_up(o); return res; } } int n,euler[maxn*2],id[maxn][2],sum[maxn*2],deapth[maxn*2],tot = 1,pre[maxn],father[maxn]; vector<Edge>New_next[maxn],e; bool vis[maxn]; querynode q[maxn]; vector<tarjan>use[maxn]; inline int find_set(int x) { return x != pre[x] ? pre[x] = find_set(pre[x]) : x; } inline void union_set(int x,int y) { pre[find_set(x)] = y; } void init_id(int cur) { id[cur][0] = tot++; for(int i = 0 ; i < New_next[cur].size() ; ++ i) { int nextnode = New_next[cur][i].v; if (nextnode == father[cur]) continue; init_id(nextnode); } id[cur][1] = tot++; } void init_tarjan(int cur) { pre[cur] = cur; vis[cur] = true; for(int i = 0 ; i < New_next[cur].size() ; ++ i) { int nextnode = New_next[cur][i].v; if (nextnode == father[cur]) continue; init_tarjan(nextnode); union_set(nextnode,cur); } for(int i = 0 ; i < use[cur].size() ; ++ i) { int v = use[cur][i].v , ansid = use[cur][i].ansid; if (vis[v]) q[ansid].lca = find_set(v); } } void fatherset(int cur,int fat) { father[cur] = fat; for(int i = 0 ; i < New_next[cur].size() ; ++ i) { int nextnode = New_next[cur][i].v; if (nextnode == fat) continue; fatherset(nextnode,cur); } } int main(int argc,char *argv[]) { scanf("%d",&n); memset(sum,0,sizeof(sum)); memset(vis,false,sizeof(vis)); for(int i = 0 ; i < n - 1 ; ++ i) { int u,v,w; scanf("%d%d%d",&u,&v,&w); e.pb(Edge(u,v,w)); New_next[u].pb(Edge(u,v,w)); New_next[v].pb(Edge(v,u,w)); } fatherset(1,0); init_id(1); build_tree(1,1,tot+5); for(int i = 0 ; i < e.size() ; ++ i) { int u = e[i].u , v = e[i].v , w = e[i].w; if (father[u] == v) { swap(u,v); swap(e[i].u,e[i].v); } updata(id[v][0]+1,id[v][1],1,w); } int qnumber; scanf("%d",&qnumber); for(int i = 0 ; i < qnumber ; ++ i) { int type,t1,t2; scanf("%d%d%d",&type,&t1,&t2); q[i].type = type,q[i].t1 = t1 , q[i].t2 = t2; if (type & 1) { if (father[t1] == t2) swap(q[i].t1,q[i].t2); use[t1].pb(tarjan(t2,i)); use[t2].pb(tarjan(t1,i)); } } init_tarjan(1); //离线处理 for(int i = 0 ; i < qnumber ; ++ i) { int type = q[i].type ,t1 = q[i].t1 ,t2 = q[i].t2 ; if (type & 1) { int lca = q[i].lca; int p1 = querysum(id[t1][1],id[t1][1],1); int p2 = querysum(id[t2][1],id[t2][1],1); int p3 = querysum(id[lca][1],id[lca][1],1); printf("%d\n",p1+p2-2*p3); } else { int u = e[t1-1].u, v = e[t1-1].v; updata(id[v][0]+1,id[v][1],1, t2 - e[t1-1].w); e[t1-1].w = t2; } } return 0; }
UESTC_树上的距离 2015 UESTC Training for Graph Theory<Problem E>
标签:
原文地址:http://www.cnblogs.com/Xiper/p/4570658.html