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;
}

 

posted @ 2018-05-19 15:36  天之道,利而不害  阅读(248)  评论(0编辑  收藏  举报