CodeChef DGCD
You're given a tree on N vertices. Each vertex has a positive integer written on it, number on the ith vertex being vi. Your program must process two types of queries :
1. Find query represented by F u v : Find out gcdof all numbers on the unique path between vertices u and v in the tree (both inclusive).
2. Change query represented by C u v d : Add d to the number written on all vertices along the unique path between vertices u and v in the tree (both inclusive).
Input
First line of input contains an integer Ndenoting the size of the vertex set of the tree. Then follow N - 1 lines, ith of which contains two integers ai and bi denoting an edge between vertices ai and bi in the tree. After this follow N space separated integers in a single line denoting initial values vi at each of these nodes. Then follows a single integer Q on a line by itself, denoting the number of queries to follow. Then follow Q queries, each one on a line by itself. Each query is either a find query or a change query with format as given in problem statement. Note that all vertices are 0-based.
Output
For every find query, print the answer to that query in one line by itself.
Example
Input: 6 0 4 0 5 1 5 5 2 3 5 3 1 3 2 4 2 5 F 3 5 C 1 3 1 C 3 4 4 F 3 0 F 2 5 Output: 2 7 1
大致题意:给一棵树,每个点都有点权.现在有两种操作:1.将u到v的路径上的点的点权都加k. 2.求u到v的路径上的点的gcd.
分析:两个点之间的路径,很容易想到树链剖分.常见的套路用线段树维护,但是这样的话就不好处理区间加了.需要用到gcd的一个性质:
gcd(a,b,c,d......) = gcd(a,b-c,c-d,......).也就是说只需要知道首元素的值和差分后序列的gcd就可以了.因为差分过了,所以修改操作可以直接变成单点修改.在pushup的时候就能更新gcd.这样的话会有一个问题.差分修改一个区间的后继,在树上,一个点可能有多个子节点,那么它的后继是谁呢?很容易就能想到是重儿子,因为我们查询和修改每次都是从重链往上跳,用到的自然就是重儿子的值.那么整体的思路就是用线段树维护差分+区间修改+单点查询.
说起来挺容易的,其实写起来比较复杂.首先差分有可能将数变成负的,gcd求出来就可能是一个负数,解决方法就是在最后输出答案的时候加一个abs. 第二个就是这道题建树的时候因为是要建差分序列,所以要在第二次dfs的时候处理出每个id对应的值(见代码),再一个就是查询的时候可能会遇到空区间,要及时特判掉. 最坑的就是线段树的标记下传会T掉!这道题卡时卡的丧心病狂,正确的做法是将一路的标记直接加上,不下传.最好使用快读.
调了一天QAQ,不过最后终于卡着时过了.
#include <cstdio> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 50010; int n,q,head[maxn],to[maxn * 2],nextt[maxn * 2],tot = 1,v[maxn]; int dep[maxn],sizee[maxn],top[maxn],cnt,son[maxn],id[maxn],idx[maxn],fa[maxn]; int c[maxn << 2],tag[maxn << 2],g[maxn << 2],val[maxn]; inline int read() { int ans,f=1;char ch; while ((ch=getchar())<'0'||ch>'9') if (ch=='-') f=-1;ans=ch-'0'; while ((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0'; return ans*f; } int gcd(int a,int b) { if (!b) return a; return gcd(b,a % b); } void add(int x,int y) { to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } void dfs(int u,int d,int from) { dep[u] = d; sizee[u] = 1; fa[u] = from; for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (v == from) continue; dfs(v,d + 1,u); sizee[u] += sizee[v]; if (sizee[v] >= sizee[son[u]]) son[u] = v; } } void dfs2(int u,int topp) { top[u] = topp; id[u] = ++cnt; val[cnt] = v[u]; if (son[u]) dfs2(son[u],topp); for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (v == son[u] || v == fa[u]) continue; dfs2(v,v); } } void pushup(int o) { g[o] = gcd(g[o * 2],g[o * 2 + 1]); } void build(int o,int l,int r) { if (l == r) { c[o] = val[l]; g[o] = val[l] - val[l - 1]; return; } int mid = (l + r) >>1; build(o * 2,l,mid); build(o * 2 + 1,mid + 1,r); pushup(o); } void update1(int o,int l,int r,int x,int y,int v) //权值区间修改 { if (x <= l && r <= y) { c[o] += v; return; } int mid = (l + r) >> 1; if (x <= mid) update1(o * 2,l,mid,x,y,v); if (y > mid) update1(o * 2 + 1,mid + 1,r,x,y,v); } void update2(int o,int l,int r,int cur,int v) //gcd单点修改 { if (l == r) { g[o] += v; return; } pushdown(o); int mid = (l + r) >> 1; if (cur <= mid) update2(o * 2,l,mid,cur,v); if (cur > mid) update2(o * 2 + 1,mid + 1,r,cur,v); pushup(o); } int query1(int o,int l,int r,int cur) //单点询问权值 { if (l == r) return c[o]; pushdown(o); int mid = (l + r) >> 1; if (cur <= mid) return c[o] + query1(o * 2,l,mid,cur); if (cur > mid) return c[o] + query1(o * 2 + 1,mid + 1,r,cur); } int query2(int o,int l,int r,int x,int y) //询问区间gcd { if (x > y) return 0; if (x <= l && r <= y) return g[o]; pushdown(o); int mid = (l + r) >> 1,res = 0; if (x <= mid) { int t = query2(o * 2,l,mid,x,y); if (!res) res = t; } if (y > mid) { int t = query2(o *2 + 1,mid + 1,r,x,y); if (!res) res = t; else res = gcd(res,t); } return res; } int solve1(int x,int y) { int ans = 0; while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) swap(x,y); ans = gcd(ans,query1(1,1,n,id[top[x]])); ans = gcd(ans,query2(1,1,n,id[top[x]] + 1,id[x])); x = fa[top[x]]; } if (dep[x] > dep[y]) swap(x,y); ans = gcd(ans,query1(1,1,n,id[x])); ans = gcd(ans,query2(1,1,n,id[x] + 1,id[y])); return abs(ans); } void solve2(int x,int y,int d) { while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) swap(x,y); update1(1,1,n,id[top[x]],id[x],d); update2(1,1,n,id[top[x]],d); update2(1,1,n,id[x] + 1,-d); x = fa[top[x]]; } if (dep[x] > dep[y]) swap(x,y); update1(1,1,n,id[x],id[y],d); update2(1,1,n,id[x],d); update2(1,1,n,id[y] + 1,-d); } int main() { n = read(); for (int i = 1; i < n; i++) { int u,v; u = read(),v = read(); u++; v++; add(u,v); add(v,u); } for (int i = 1; i <= n; i++) v[i] = read(); dfs(1,1,0); dfs2(1,1); build(1,1,n); q = read(); while (q--) { char s[2]; int a,b,c; scanf("%s",s); if (s[0] == 'F') { a = read(); b = read(); a++; b++; printf("%d\n",solve1(a,b)); } else { a = read(); b = read(); c = read(); a++; b++; solve2(a,b,c); } } return 0; }