ACM Tax
题目:http://codeforces.com/gym/101161
题解:树上一条链的第k大,在DFS时Update,每颗线段树维护的是:树的根到这个节点的一条链,然后这多颗线段树组织成了主席树。对于询问( u , v ),直接处理u,v,Lca(u,v)三条链(根到这个节点的简单路径称为链),因为线段树维护的就是链,所以主席树直接查询就行了,和区间第k大一样的道理。我之所以一直说的是线段树,因为我认为主席树是多颗线段树的组织方式而已,重点依然在线段树。此题每颗线段树维护的是一条链!!!
感受:成块的代码(比如LCA)一定要保证正确性啊,要不然好难调啊,orzzzzzzzz
#pragma warning(disable:4996) #include<queue> #include<map> #include<string> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define mem(arr,in) memset(arr,in,sizeof(arr)) using namespace std; const int maxn = 50005; int kase, n, q, tot, cnt, Max; int root[maxn], pa[maxn][20], dp[maxn], head[maxn]; struct node { int to, va, next; } e[maxn * 2]; struct code { int l, r, sum; } T[maxn * 20]; void Inite() { Max = tot = cnt = 0; mem(dp, 0); mem(pa, -1); mem(head, -1); } void addedge(int u, int v, int w) { e[tot].to = v; e[tot].va = w; e[tot].next = head[u]; head[u] = tot++; } void Update(int l, int r, int &rt, int pre, int p) { T[++cnt] = T[pre], T[cnt].sum++, rt = cnt; if (l == r) return; int mid = (l + r) >> 1; if (mid >= p) Update(l, mid, T[rt].l, T[pre].l, p); else Update(mid + 1, r, T[rt].r, T[pre].r, p); } int Query(int l, int r, int x, int y, int z, int k) { if (l == r) return l; int mid = (l + r) >> 1; int sum = T[T[x].l].sum + T[T[y].l].sum - 2 * T[T[z].l].sum; if (sum >= k) return Query(l, mid, T[x].l, T[y].l, T[z].l, k); else return Query(mid + 1, r, T[x].r, T[y].r, T[z].r, k - sum); } void DFS(int u, int p,int deep) { pa[u][0] = p; dp[u] = deep; for (int i = head[u]; i != -1; i = e[i].next) { int v = e[i].to; if (v != p) { Update(1, Max, root[v], root[u], e[i].va); DFS(v, u, deep + 1); } } } void Getpa() { DFS(1, -1, 0); for (int i = 1; (1 << i) <= n; i++) { for (int j = 1; j <= n; j++) { if (pa[j][i - 1] > 0) pa[j][i] = pa[pa[j][i - 1]][i - 1]; } } } int Lca(int u, int v) { if (dp[u] > dp[v]) swap(u, v); for (int i = 0; i <= 19; i++) if ((dp[v] - dp[u]) >> i & 1) v = pa[v][i]; //不要跳过头了 if (u == v) return u; for (int i = 19; i >= 0; i--) if (pa[u][i] != pa[v][i]) { u = pa[u][i]; v = pa[v][i]; } return pa[u][0]; } int main() { scanf("%d", &kase); while (kase--) { scanf("%d", &n); Inite(); for (int i = 2; i <= n; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); addedge(u, v, w); addedge(v, u, w); Max = max(Max, w); } Getpa(); scanf("%d", &q); for (int i = 1; i <= q; i++) { int u, v; scanf("%d%d", &u, &v); int x = Lca(u, v); int p = dp[u] + dp[v] - 2 * dp[x]; //判断这条路径上有几条边 if (p % 2) { double ans = (double)Query(1, Max, root[u], root[v], root[x], p / 2 + 1); printf("%.1lf\n", ans); } else { double ans = (double)Query(1, Max, root[u], root[v], root[x], p / 2) + (double)Query(1, Max, root[u], root[v], root[x], p / 2 + 1); printf("%.1lf\n", ans / 2); } } } return 0; }