CF375D Tree and Queries / Dsu on tree 模板
Tree and Queries
题面翻译
- 给定一棵
个节点的树,根节点为 。每个节点上有一个颜色 。 次操作。操作有一种:u k
:询问在以 为根的子树中,出现次数 的颜色有多少种。
, , 。
题目描述
You have a rooted tree consisting of
In this problem you need to answer to
You can find the definition of a rooted tree by the following link: http://en.wikipedia.org/wiki/Tree_(graph_theory).
输入格式
The first line contains two integers
Next
输出格式
Print
样例 #1
样例输入 #1
8 5
1 2 2 3 3 2 3 3
1 2
1 5
2 3
2 4
5 6
5 7
5 8
1 2
1 3
1 4
2 3
5 3
样例输出 #1
2
2
1
0
1
样例 #2
样例输入 #2
4 1
1 2 3 4
1 2
2 3
3 4
1 1
样例输出 #2
4
提示
A subtree of vertex
Solution
对于刚学
将每个点的颜色存储在
- 计算所有轻儿子的答案并清除对
和 数组的贡献。 - 计算重儿子答案并且保留对
和 的贡献。 - 将轻儿子贡献加入
和 数组并更新答案
用到这种启发式合并思想的还有按秩合并并查集(将小的集合并入大的集合,从而达到
可能参考代码更好理解:
#include<bits/stdc++.h>
using namespace std;
template<typename T> void read(T &k)
{
k=0;T flag=1;char b=getchar();
while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
while (isdigit(b)) {k=k*10+b-48;b=getchar();}
k*=flag;
}
template<typename T> void write(T k) {if (k<0) putchar('-'),write(-k);if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=1e5;
struct EDGE{
int nxt,to;
}edge[(_SIZE<<1)+5];
int tot,head[_SIZE+5];
void AddEdge(int x,int y)
{
edge[++tot]=(EDGE){head[x],y};
head[x]=tot;
}
int n,m,col[_SIZE+5];
int id[_SIZE+5],node[_SIZE+5],son[_SIZE+5],siz[_SIZE+5],dfn;
int ans[_SIZE+5];
int cnt[_SIZE+5],sum[_SIZE+5];
vector<pair<int,int> > vec[_SIZE+5];//用于存储对于每个节点的询问
void dfs1(int x,int fa)//第一遍dfs,跑dfs序,重儿子和size
{
id[x]=++dfn;//dfs序
node[dfn]=x;//dfs序对应的节点
siz[x]=1;
int maxson=-1;
for (int i=head[x];i;i=edge[i].nxt)
{
int twd=edge[i].to;
if (twd==fa) continue;
dfs1(twd,x);
siz[x]+=siz[twd];
if (siz[twd]>maxson) maxson=siz[twd],son[x]=twd;
}
}
void add(int x) {cnt[col[x]]++;sum[cnt[col[x]]]++;}//加入该点的影响
void del(int x) {sum[cnt[col[x]]]--;cnt[col[x]]--;}//清除该点的影响
void dfs2(int x,int fa,bool keep)//keep用于告知当前点对cnt和sum的贡献是否应该清除
{
for (int i=head[x];i;i=edge[i].nxt)//计算轻儿子答案
{
int twd=edge[i].to;
if (twd==fa || twd==son[x]) continue;
dfs2(twd,x,false);//不保留贡献
}
if (son[x]) dfs2(son[x],x,true);add(x);//计算重儿子答案,保留贡献,加入当前点的贡献
for (int i=head[x];i;i=edge[i].nxt)//加入轻儿子的贡献
{
int twd=edge[i].to;
if (twd==fa || twd==son[x]) continue;
for (int j=id[twd];j<=id[twd]+siz[twd]-1;j++) add(node[j]);//同一子树dfs序连续,直接for即可
}
for (auto i:vec[x]) ans[i.first]=sum[i.second];//遍历当前节点x的询问并更新答案
if (!keep) for (int i=id[x];i<=id[x]+siz[x]-1;i++) del(node[i]);//如果需要不保留贡献则将当前节点的子树全部清除贡献
}
int main()
{
read(n);read(m);
for (int i=1;i<=n;i++) read(col[i]);
for (int i=1;i<n;i++)
{
int u,v;read(u),read(v);
AddEdge(u,v);AddEdge(v,u);
}
for (int i=1;i<=m;i++)
{
int u,k;read(u),read(k);
vec[u].push_back(make_pair(i,k));//存储id和k到对应的u节点上
}
dfs1(1,0);
dfs2(1,0,1);
for (int i=1;i<=m;i++) writewith(ans[i],'\n');puts("");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步