Codeforces 375D Tree and Queries(DFS序+莫队+树状数组)
题目链接 Tree and Queries
题目大意 给出一棵树和每个节点的颜色。每次询问$vj, kj$
你需要回答在以$vj$为根的子树中满足条件的的颜色数目,
条件:具有该颜色的节点数量至少为$kj$。
(莫队居然可以过)
首先转$DFS$序,这样就变成了区间查询。
然后直接套用莫队,求出每次询问状态下的$t[],t[k]$表示当前区间内拥有$k$个节点的颜色数量。
然后统计$t[k] + t[k + 1], ..., t[MAX]$即可,这个过程用树状数组维护。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e5 + 10; const int d = N + 10; int b[N], c[N * 3], f[N]; int in[N], out[N]; int a[N], belong[N]; int n, m, ti = 0, bs = 0; int l, r; int ans[N]; vector <int> v[N]; struct node{ int v, k, id; int l, r; friend bool operator < (const node &a, const node &b){ return belong[a.l] == belong[b.l] ? a.r < b.r : belong[a.l] < belong[b.l]; } } q[N]; inline void update(int x, int val){ for (; x <= (N * 3 - 10); x += x & -x) c[x] += val; } inline int query(int x){ if (x == 0) return c[x]; int ret = 0; for (; x; x -= x & -x) ret += c[x]; return ret; } void dfs(int x, int fa){ in[x] = ++ti; for (auto u : v[x]){ if (u == fa) continue; dfs(u, x); } out[x] = ti; } int main(){ scanf("%d%d", &n, &m); rep(i, 1, n) scanf("%d", a + i); rep(i, 2, n){ int x, y; scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x); } dfs(1, 0); rep(i, 1, n) b[in[i]] = i; rep(i, 1, m){ scanf("%d%d", &q[i].v, &q[i].k); q[i].id = i; q[i].l = in[q[i].v]; q[i].r = out[q[i].v]; } bs = sqrt(n); rep(i, 1, n) belong[i] = (i - 1) / bs + 1; sort(q + 1, q + m + 1); update(d, n); l = 1, r = 0; rep(i, 1, m){ while (l > q[i].l){ --l; update(f[a[b[l]]] + d, -1); ++f[a[b[l]]]; update(f[a[b[l]]] + d, 1); } while (l < q[i].l){ update(f[a[b[l]]] + d, -1); --f[a[b[l]]]; update(f[a[b[l]]] + d, 1); ++l; } while (r < q[i].r){ ++r; update(f[a[b[r]]] + d, -1); ++f[a[b[r]]]; update(f[a[b[r]]] + d, 1); } while (r > q[i].r){ update(f[a[b[r]]] + d, -1); --f[a[b[r]]]; update(f[a[b[r]]] + d, 1); --r; } ans[q[i].id] = max(0, query(N * 3 - 10) - query(q[i].k - 1 + d)); } rep(i, 1, m) printf("%d\n", ans[i]); return 0; }