以前迷迷糊糊用dsu on tree写的题目 但是其实没搞明白 现在换一种写(太菜了还是没搞明白dsu on tree)
题意:
给你一棵树,询问给定询问的节点上,子树内距离小于等于k的节点不同颜色的种类有多少个。k是固定的值。
解法:
本题做法为比较板子的线段树合并,如果您没有接触过,可以先写几道简单的学习一下。
对线段树维护颜色的数量,对于一个节点,它要给自己代表的颜色+1,要给它往上数k+1个点的位置把自己的颜色-1
然后线段树维护每个颜色出现的数量以及出现颜色的种类,这个很好写,要合并所以需要动态开点。
一个点往上数k个点就是用树上倍增往上面跑 是log的
问题需要离线。
在dfs的过程中,需要把子树的树合并到父亲上,然后处理点的+1-1,然后处理询问。
由于是线段树合并,复杂度取决于小的那个树的大小,因此整体的时间和空间复杂度都是nlogn
附上ac代码
seg表示某个颜色的出现次数
sum表示所有颜色中 出现了的颜色的总数
u点表示以u为根节点,子树中所有距离小于等于k的节点出现的颜色信息的线段树的根节点(所以区间查就查sum[u] 就行了)
#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 1e5+7; const ll mod = 1e9+7; const ll inf = 0x3f3f3f3f; const ll R = 40; int dep[MAXN], fa[MAXN][22];int n,k; int c[MAXN]; vector<int> adj[MAXN]; int cnt=0; vector<int> Q[MAXN]; vector<int> op[MAXN]; int seg[MAXN*R],ls[MAXN*R],rs[MAXN*R],sum[MAXN*R]; int ans[MAXN]; void dfs(int u, int par, int w) { dep[u] = dep[par] + 1; fa[u][0] = par; for (int i = 1; i <= 20; ++i) { fa[u][i] = fa[fa[u][i - 1]][i - 1]; } for (auto &v: adj[u]) { if (v == par) continue; dfs(v, u, w); } } int find(int x){ int tmp=k+1; for(int i=20;i>=0;i--){ if((1LL<<i)<=tmp){ tmp-=(1LL<<i); x=fa[x][i]; } } return x; } void up(int id){ sum[id]=sum[ls[id]]+sum[rs[id]]; } int merge(int a,int b,int l,int r){ if(!a) return b; if(!b) return a; if(l==r){ seg[a]+=seg[b]; sum[a]=max(sum[a],sum[b]); return a; } int mid=l+r>>1; ls[a]=merge(ls[a],ls[b],l,mid); rs[a]=merge(rs[a],rs[b],mid+1,r); up(a); return a; } void insert(int &id,int l,int r,int pos,int val){ if(!id) id=++cnt; if(l==r){ if(val==1&&seg[id]==0){ sum[id]=1; } else if(val==-1&&seg[id]==1){ sum[id]=0; } seg[id]+=val; return; } int mid=l+r>>1; if(pos<=mid) insert(ls[id],l,mid,pos,val); else insert(rs[id],mid+1,r,pos,val); up(id); } void dfs(int u,int fa){ for(auto &v:adj[u]){ if(v==fa) continue; dfs(v,u); merge(u,v,1,n); } insert(u,1,n,c[u],1); for(auto &it:op[u]) insert(u,1,n,it,-1); for(auto &it:Q[u]){ ans[it]=sum[u]; } } void solve(){ cin>>n>>k; cnt=n; for(int i=1;i<=n;i++){ cin>>c[i]; } for(int i=1;i<n;i++){ int u,v;cin>>u>>v; adj[u].push_back(v); adj[v].push_back(u); } dfs(1,0,0); for(int i=1;i<=n;i++){ if(dep[i]<=k+1) continue; int x=find(i); op[x].push_back(c[i]); } int q;cin>>q; for(int i=1;i<=q;i++){ int x;cin>>x; Q[x].push_back(i); } dfs(1,0); for(int i=1;i<=q;i++) cout<<ans[i]<<"\n"; } signed main(){ close; solve(); } /* 7 2 1 1 2 1 1 1 2 1 2 1 3 2 4 2 5 5 7 3 6 7 1 2 3 4 5 6 7 */View Code
标签:ll,女生,2022CCPC,int,线段,MAXN,颜色,id From: https://www.cnblogs.com/xishuiw/p/18004799