HDU 5799 This world need more Zhu(树上莫队)

题意:

$给定一颗N\le 10^5个点的树,点权A_i\le 10^9,Q\le 10^5,询问形如op u v a b$
$op=1时,u=v,查询子树u中,gcd(\sum_{cnt[x]=a} x, \sum_{cnt[y]=b} y)$
$op=2时,查询路径(u, v)中,gcd(\sum_{cnt[x]=a} x, \sum_{cnt[y]=b} y)$

分析:

$op=1,dfs序搞成序列上的莫队,op=2树上莫队$
$板题\times 2$

//
//  Created by TaoSama on 2016-08-08
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int B = 250;

typedef long long LL;
int n, m, a[N], b[N];

struct Edge {
    int v, nxt;
} edge[N << 1];
int head[N], eCnt;

void addEdge(int u, int v) {
    edge[eCnt] = {v, head[u]};
    head[u] = eCnt++;
}

int L[N], R[N], dep[N], fa[N], vs[N], dfsNum;
int id[N], blocks;
int stk[N], top;

void dfs(int u, int f) {
    L[u] = ++dfsNum;
    vs[dfsNum] = u;
    fa[u] = f;

    int btm = top;
    for(int i = head[u]; ~i; i = edge[i].nxt) {
        int v = edge[i].v;
        if(v == f) continue;

        dep[v] = dep[u] + 1;
        dfs(v, u);

        if(top - btm >= B) {
            ++blocks;
            while(top != btm) {
                int v = stk[top--];
                id[v] = blocks;
            }
        }
    }
    R[u] = dfsNum;
    stk[++top] = u;
}

struct Query {
    int l, r, block, a, b, id;
};

bool cmpSeq(const Query& a, const Query& b) {
    return a.block < b.block ||
           a.block == b.block && a.r < b.r;
}
bool cmpTree(const Query& a, const Query& b) {
    return id[a.l] < id[b.l] ||
           id[a.l] == id[b.l] && L[a.r] < L[b.r];
}

int cnt[N];
LL sum[N], ans[N];

void add(int x) {
    sum[cnt[b[x]]] -= a[x];
    ++cnt[b[x]];
    sum[cnt[b[x]]] += a[x];
}

void del(int x) {
    sum[cnt[b[x]]] -= a[x];
    --cnt[b[x]];
    sum[cnt[b[x]]] += a[x];
}

bool in[N];
int cross;
void reverse(int x) {
    if(in[x]) {
        in[x] = false;
        del(x);
    } else {
        in[x] = true;
        add(x);
    }
}

void moveUp(int& x) {
    if(!cross) {
        if(in[x] && !in[fa[x]]) cross = x;
        else if(in[fa[x]] && !in[x]) cross = fa[x];
    }
    reverse(x); x = fa[x];
}

void move(int a, int b) {
    if(a == b) return;
    cross = 0;
    if(in[b]) cross = b;
    while(dep[a] > dep[b]) moveUp(a);
    while(dep[b] > dep[a]) moveUp(b);
    while(a != b) moveUp(a), moveUp(b);
    reverse(a); reverse(cross);
}

void gao() {
    dfsNum = blocks = 0;
    dfs(1, 0);
    while(top) id[stk[top--]] = blocks;
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);

        vector<int> xs(n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            xs[i - 1] = a[i];
        }
        sort(xs.begin(), xs.end());
        xs.resize(unique(xs.begin(), xs.end()) - xs.begin());
        for(int i = 1; i <= n; ++i)
            b[i] = lower_bound(xs.begin(), xs.end(), a[i]) - xs.begin() + 1;

        eCnt = 0; memset(head, -1, sizeof head);
        for(int i = 1; i < n; ++i) {
            int u, v; scanf("%d%d", &u, &v);
            addEdge(u, v);
            addEdge(v, u);
        }

        gao();

        vector<Query> qs, qt;
        for(int i = 1; i <= m; ++i) {
            int op, u, v, a, b;
            scanf("%d%d%d%d%d", &op, &u, &v, &a, &b);
            if(op == 1) {
                int p = L[u] / B;
                qs.push_back({L[u], R[u], p, a, b, i});
            } else {
                if(id[u] > id[v]) swap(u, v);
                qt.push_back({u, v, -1, a, b, i});
            }
        }

        {
            //subtree
            sort(qs.begin(), qs.end(), cmpSeq);
            memset(cnt, 0, sizeof cnt);
            memset(sum, 0, sizeof sum);

            int l = 1, r = 0;
            for(auto& q : qs) {
                while(r < q.r) add(vs[++r]);
                while(l < q.l) del(vs[l++]);
                while(r > q.r) del(vs[r--]);
                while(l > q.l) add(vs[--l]);

                ans[q.id] = __gcd(sum[q.a], sum[q.b]);
            }
        }

        {
            //path
            sort(qt.begin(), qt.end(), cmpTree);
            memset(cnt, 0, sizeof cnt);
            memset(sum, 0, sizeof sum);
            memset(in, 0, sizeof in);

            add(1); in[1] = true;
            int l = 1, r = 0;
            for(auto& q : qt) {
                move(l, q.l);
                move(r, q.r);
                l = q.l; r = q.r;

                ans[q.id] = __gcd(sum[q.a], sum[q.b]);
            }
        }

        static int kase = 0;
        printf("Case #%d:\n", ++kase);
        for(int i = 1; i <= m; ++i) printf("%I64d\n", ans[i]);
    }

    return 0;
}