题目传送门
题意:
个点的树,每个点都有一个颜色 ,有 个询问。
每次询问:在以第 个节点为根的子树中,出现次数多于 次的颜色的种类数。
数据范围: 。
题解:
首先掏出树上启发式合并的板子。
然后每个节点的子树维护两个信息: 表示颜色是 的节点出现的次数, 表示出现次数是 次的颜色的种类数。
每次询问我们需要知道的是 。所以需要线段树维护后缀和。
感受:
这道题在此刻是有锅的,至少我是这么认为的。
因为你暴力计算 也是可以通过的,但是显然上限设置为 是不对的。而且时间也不应该可以过去。
我构造了一个大数据, 个节点,颜色都是 。然后询问在以第 个节点为根的子树中出现次数至少是 的颜色的种类数。显然应该是 ,但是有的输出是 的也过去了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int , int> pii ;
const int maxn = 1e5 + 5;
int n , m , c[maxn] ;
struct seg_tree
{
int sum[maxn << 2] ;
int ls(int x)
{
return x << 1 ;
}
int rs(int x)
{
return x << 1 | 1 ;
}
void build(int id , int l , int r)
{
if(l == r){sum[id] = c[l] ; return ;}
int mid = (l + r) >> 1 ;
build(ls(id) , l , mid) ;
build(rs(id) , mid + 1 , r) ;
sum[id] = sum[ls(id)] + sum[rs(id)] ;
}
void update(int id , int l , int r , int x , int y)
{
int mid = (l + r) / 2 ;
if(l == r && l == x){sum[id] += y ; return ;}
if(x <= mid) update(ls(id) , l , mid , x , y) ;
else update(rs(id) , mid + 1 , r , x , y) ;
sum[id] = sum[ls(id)] + sum[rs(id)] ;
}
int query(int id , int l , int r , int x , int y)
{
int ans = 0 ;
int mid = (l + r) / 2 ;
if(x <= l && r <= y) return sum[id] ;
if(x <= mid) ans += query(ls(id) , l , mid , x , y) ;
if(y > mid) ans += query(rs(id) , mid + 1 , r , x , y) ;
return ans ;
}
} seg ;
struct Link
{
int num , head[maxn] ;
struct Edge
{
int v , next ;
} edge[maxn << 1] ;
void init()
{
num = 0 ;
memset(head , -1 , sizeof(head)) ;
}
void add_edge(int u , int v)
{
edge[num].v = v ;
edge[num].next = head[u] ;
head[u] = num ++ ;
}
} link ;
struct Dsu
{
int siz[maxn] , son[maxn] ;
int flag ;
int ans[maxn] , cnt[maxn] , num[maxn] ;
vector<pii> q[maxn] ;
void init()
{
flag = 0 ;
memset(siz , 0 , sizeof(siz)) ;
memset(son , 0 , sizeof(son)) ;
memset(ans , 0 , sizeof(ans)) ;
memset(cnt , 0 , sizeof(cnt)) ;
memset(num , 0 , sizeof(num)) ;
}
void dfs1(int f , int u)
{
siz[u] = 1 ;
for(int i = link.head[u] ; i != -1 ; i = link.edge[i].next)
{
int v = link.edge[i].v ;
if(v == f) continue ;
dfs1(u , v) ;
siz[u] += siz[v] ;
if(siz[v] > siz[son[u]]) son[u] = v ;
}
}
void add(int f , int u , int x)
{
int up = 1e5 ;
if(cnt[c[u]] > 0) seg.update(1 , 1 , up , cnt[c[u]] , -1) ;
cnt[c[u]] += x ;
if(cnt[c[u]] > 0) seg.update(1 , 1 , up , cnt[c[u]] , 1) ;
for(int i = link.head[u] ; i != -1 ; i = link.edge[i].next)
{
int v = link.edge[i].v ;
if(v == f || v == flag) continue ;
add(u , v , x) ;
}
}
void dfs2(int f , int u , int keep)
{
for(int i = link.head[u] ; i != -1 ; i = link.edge[i].next)
{
int v = link.edge[i].v ;
if(v == f || v == son[u]) continue ;
dfs2(u , v , 0) ;
}
if(son[u]) dfs2(u , son[u] , 1) , flag = son[u] ;
add(f , u , 1) ;
for(auto x : q[u])
{
int id = x.first ;
int k = x.second ;
int up = 1e5 ;
ans[id] += seg.query(1 , 1 , up , k , up) ;
}
if(son[u]) flag = 0 ;
if(!keep) add(f , u , -1) ;
}
void print()
{
for(int i = 1 ; i <= m ; i ++) printf("%d\n" , ans[i]) ;
}
} dsu ;
int main()
{
scanf("%d%d", &n , &m) ;
for(int i = 1 ; i <= n ; i ++) scanf("%d" , &c[i]) ;
link.init() ;
for(int i = 1 ; i <= n - 1 ; i ++)
{
int u , v ;
scanf("%d%d" , &u , &v) ;
link.add_edge(u , v) , link.add_edge(v , u) ;
}
for(int i = 1 ; i <= m ; i ++)
{
int v , k ;
scanf("%d%d" , &v , &k) ;
dsu.q[v].push_back(make_pair(i , k)) ;
}
dsu.init() ;
dsu.dfs1(0 , 1) ;
dsu.dfs2(0 , 1 , 0) ;
dsu.print() ;
return 0;
}