D. Tree and Queries (dfs序+莫队 /树上启发式合并)

传送门

在这里插入图片描述

可以离线,子树查询,数据范围是1e5。

全局维护num[i]表示颜色i出现的次数,cnt[i]表示出现次数大于等于i次的颜色种类数。
由于num[i]每次都是加1,减1,变化是连续的,所以cnt数组的维护只需要单点修改:
当新加入颜色x,num[x]+1这个数增加1个,num[x]这个数减少1个,
所以cnt[num[x]+1]+1,而cnt[num[x]]不变。
同理,当减去颜色x,cnt[num[x]]-1

代码略。

如果考虑用树上启发式,我们同样维护num[i],cnt[i],含义一样。
当然如果用cnt[i]表示出现次数恰好等于i次的颜色种类数,那么每次查询都是区间查询,需要对cnt这个桶,建线段树维护。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define pdd pair<double, double>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e17+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
int ret,num[maxn];//num[i] 颜色i的节点数
int cnt[maxn];//节点数大于等于i的颜色种类数  u加进去  num[u]++  cnt[num[u]]++  查询就cnt[k]
int dep[maxn],son[maxn],siz[maxn];
int in[maxn],clk,pos[maxn];
vector<int> g[maxn];
int c[maxn];
vector<pii> q[maxn];
int n,m;
int ans[maxn];
void dfs(int rt,int fa) 
{
    
    
	dep[rt]=dep[fa]+1;
	in[rt]=++clk;
	pos[clk]=rt;
	siz[rt]=1;
	for(int i:g[rt]) 
	{
    
    
		if(i==fa) continue;
		dfs(i,rt);
		siz[rt]+=siz[i];
		if(siz[i] > siz[son[rt]]) son[rt]=i;
	}
}
int SON;

void add(int rt,int v) 
{
    
    
	for(int i=in[rt];i<in[rt]+siz[rt];i++)
	{
    
    
		int u=pos[i];
		if(u==SON) i=i+siz[SON]-1;
		else 
		{
    
    
			if(v==1) 
			{
    
    
				num[c[u]]++;
				cnt[num[c[u]]]++;
			}
			else 
			{
    
    
				num[c[u]]--;
				cnt[num[c[u]]+1]--;
			}
		}
	}
}
void dfs2(int rt,int fa,bool save)
{
    
    
	for(int i:g[rt]) 
	{
    
    
		if(i==son[rt] || i==fa) continue;
		dfs2(i,rt,0);
	}
	if(son[rt]) dfs2(son[rt],rt,1),SON=son[rt];
	add(rt,1),SON=0;
	for(auto it:q[rt]) 
	{
    
    
		ans[it.second]=cnt[it.first];
	}
	if(!save) 
	{
    
    
		add(rt,-1);
	}
}
int main()
{
    
    
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",c+i);
	for(int i=2,u,v;i<=n;i++) 
	{
    
    
		scanf("%d %d",&u,&v);
		g[u].push_back(v),g[v].push_back(u);
	}
	dfs(1,0);
	for(int i=1;i<=m;i++)
	{
    
    
		int x,k;
		scanf("%d %d",&x,&k);
		q[x].push_back({
    
    k,i});
	}
	dfs2(1,0,1);
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/120599107
今日推荐