期末考前写题解,\(rp++! \ rp++! \ rp++!\)
\[
description
\]
给出一个以 \(1\) 为根的边带权有根树,给定一个参数 \(L\) ,问每个点的子树中与它距离小于等于 \(L\) 的节点个数。
\[
solution
\]
有关子树内的统计,肯定能联想到 线段树合并 吧。
记 \(d[u]\) 表示根节点到 \(u\) 的距离,该数组可通过一次遍历求出。
则对于每个点 \(u\) ,它的贡献就是满足下式的点对数量:
\[
d[v]-d[u]\leq L \ \{ v\in\operatorname{subtree}(u) \}
\]
移项,可化简为:
\[
d[v]\leq d[u]+L \ \{ v\in\operatorname{subtree}(u) \}
\]
那么问题就转化成了:
令点 \(u\) 的点权为 \(d[u]\)。对于每个点 \(u\) ,求出该点的子树中点权小于等于 \(d[u]+L\) 的节点个数
然后发现是一道 线段树合并 板子题,直接码上就行。
需注意的是,这个 \(L\) 和 \(d[~]\) 的范围有 \(1e18\) ,所以我们需要离散化一下防止 \(MLE\) 。
我是把每个 \(d[u]\) 以及 \(d[u]+L\) 都扔进去离散化了,如果有哪位 \(dalao\) 有更优秀的离散化方法,可以评论在博客下方,我会感激不尽。
\[
code
\]
#include<cstdio> #include<algorithm> #define RI register int using namespace std; const int N=200100,M=200100,MLOGN=10001000; int n,m; long long L; int Etot,head[N],ver[M],Next[M]; long long edge[M]; void add(int u,int v,long long w) { ver[++Etot]=v; edge[Etot]=w; Next[Etot]=head[u]; head[u]=Etot; } long long d[N]; int len; long long mapval[1000100]; void discrete() { sort(mapval+1,mapval+1+len); len=unique(mapval+1,mapval+1+len)-mapval-1; } int query(long long x) { return lower_bound(mapval+1,mapval+1+len,x)-mapval; } int tot,root[N]; struct SegmentTree{ int lc,rc; int cnt; }t[MLOGN]; int New() { tot++; t[tot].lc=t[tot].rc=t[tot].cnt=0; return tot; } void insert(int &p,int l,int r,int delta,int val) { if(!p)p=New(); t[p].cnt+=val; if(l==r)return; int mid=(l+r)/2; if(delta<=mid) insert(t[p].lc,l,mid,delta,val); else insert(t[p].rc,mid+1,r,delta,val); } int merge(int p,int q) { if(!p||!q)return p^q; t[p].cnt+=t[q].cnt; t[p].lc=merge(t[p].lc,t[q].lc); t[p].rc=merge(t[p].rc,t[q].rc); return p; } int ask(int p,int l,int r,int s,int e) { if(!p)return 0; if(s<=l&&r<=e)return t[p].cnt; int mid=(l+r)/2; int val=0; if(s<=mid) val+=ask(t[p].lc,l,mid,s,e); if(mid<e) val+=ask(t[p].rc,mid+1,r,s,e); return val; } void dfs1(int u) { mapval[++len]=d[u],mapval[++len]=d[u]+L; for(RI i=head[u];i;i=Next[i]) { int v=ver[i]; long long w=edge[i]; d[v]=d[u]+w; dfs1(v); } } int ans[N]; void dfs2(int u) { for(RI i=head[u];i;i=Next[i]) { int v=ver[i]; dfs2(v); root[u]=merge(root[u],root[v]); } insert(root[u],1,len,query(d[u]),1); ans[u]=ask(root[u],1,len,1,query(d[u]+L)); } int main() { scanf("%d%lld",&n,&L); for(RI i=2;i<=n;i++) { int fa; long long w; scanf("%d%lld",&fa,&w); add(fa,i,w); } dfs1(1); discrete(); dfs2(1); for(RI i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }
\[ thanks \ for \ watching \]
来源:https://www.cnblogs.com/cjtcalc/p/12216676.html