http://codeforces.com/contest/375/problem/D
给一棵有根树,每个节点有个颜色,每次询问 v,k,即v节点为根的子树中,有多少种颜色,出现的次数超过k。
对于查询v的子树,先转为dfs序。
对于出现次数超过k的答案,我们另外用一个数组维护,即当color1个数增加时,那么num[ color1_num ]++,
减少时则 num[ color1_num ]--
这样我们就能直接得到 num[k]为出现次数超过k次的颜色种类。
这样的话,可以发现对于区间【l,r】的转移符合使用莫队算法的条件,则可以用莫队算法,分块,区间排序。
复杂度 (q+n)sqrtn 跑了233ms
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <set> #include <vector> #include <iostream> using namespace std; #define N 100100 typedef long long ll; int a[N], cnt[N]; ll ans[N], res; int L, R; vector< int > mp[N]; int in[N]; int out[N]; int id=0; int col[N]; int who[N]; int vis[N]; int num[N]; void dfs(int x) { int i; in[x]=++id; who[id]=x; vis[x]=1; for (i=0; i<mp[x].size(); i++) { int v=mp[x][i]; if (!vis[v]) dfs(v); } out[x]=id; } struct node { int x, y, l, p,k; } q[N]; bool cmp(const node &x, const node &y) { if (x.l == y.l) return x.y < y.y; return x.l < y.l; } void query(int x, int y, int flag ) { if (flag) { for (int i=x; i<L; i++) { cnt[ col[who[i]] ]++; num[cnt[ col[who[i]] ]]++; } for (int i=L; i<x; i++) { num[cnt[ col[who[i]] ]]--; cnt[ col[who[i]] ]--; } for (int i=y+1; i<=R; i++) { num[cnt[ col[who[i]] ]]--; cnt[ col[who[i]] ]--; } for (int i=R+1; i<=y; i++) { cnt[ col[who[i]] ]++; num[cnt[ col[who[i]] ]]++; } } else { for (int i=x; i<=y; i++) { cnt[ col[who[i]] ]++; num[cnt[ col[who[i]] ]]++; } } L = x, R = y; } int main() { int n, m; scanf("%d%d", &n, &m); int x,y; for (int i=1; i<=n ; i++) scanf("%d",&col[i]); for (int i=1; i<=n-1; i++) { scanf("%d%d", &x,&y); mp[x].push_back(y); mp[y].push_back(x); } dfs(1); int block_size = sqrt(1.0*n); for (int i=0; i<m; i++) { scanf("%d%d", &x,&y); q[i].x=in[x]; q[i].y=out[x]; q[i].l = q[i].x / block_size; q[i].p = i; q[i].k=y; } sort(q, q+m, cmp); res = 0; for (int i=0; i<m; i++) { query(q[i].x, q[i].y, i); ans[q[i].p] = num[q[i].k]; } for (int i=0; i<m; i++) printf("%I64d\n", ans[i]); return 0; }