P3066 [USACO12DEC] Running Away From the Barn G
题目描述
给定一颗 \(n\) 个点的有根树,边有边权,节点从 \(1\) 至 \(n\) 编号,\(1\) 号节点是这棵树的根。
再给出一个参数 \(t\),对于树上的每个节点 \(u\),请求出 \(u\) 的子树中有多少节点满足该节点到 \(u\) 的距离不大于 \(t\)。
输入格式
输入的第一行是两个整数,分别表示节点数 \(n\) 和给出的参数 \(t\)。
第 \(2\) 到第 \(n\) 行,每行两个整数,第 \(i\) 行的整数 \(p_i, w_i\) 表示节点 \(i\) 的父节点为 \(p_i\),连结 \(i\) 与 \(p_i\) 的边的边权为 \(w_i\)。
输出格式
输出 \(n\) 行,每行一个整数,第 \(i\) 行的整数表示 \(i\) 的子树内到 \(i\) 的距离不大于 \(t\) 的节点个数。
数据规模与约定
对于全部的测试点,保证:
- \(1 \leq n \leq 2 \times 10^5\),\(1 \leq t \leq 10^{18}\)。
- \(1 \leq p_i \lt i\),\(1 \leq w_i \leq 10^{12}\)。
Solution:
线段树合并板子捏。
我们先对这整颗树求出每个点到根的距离 \(dis_{u}\) 然后对每个节点开一颗权值线段树,维护当前子树下的每个 \(dis\) 区间内的点的个数,那么到u距离不超过t就等价于:
\(dis\in[0,dis_{u}+t]\)
然后我们发现每个点都开一颗权值线段树然后遍历子树更新肯定是不行的,所以我们需要线段树合并。
然后这题就做完了
然后这题貌似还有个弱化版本this
Code:
#include<bits/stdc++.h>
#define int long long
const int N=2e5+5;
const int inf=1e17;
using namespace std;
struct Segment_Tree{int cnt,rt[N];struct Tree{int ls,rs,cnt;}t[N*40];void pushup(int x){t[x].cnt=t[t[x].ls].cnt+t[t[x].rs].cnt;}void insert(int &x,int l,int r,int pos){if(!x)x=++cnt;t[x].cnt++;if(l==r)return;int mid=l+r>>1;if(pos<=mid)insert(t[x].ls,l,mid,pos);if(mid<pos) insert(t[x].rs,mid+1,r,pos);}int merge(int x,int y,int l,int r){if(!x||!y)return x|y;if(l==r){t[x].cnt+=t[y].cnt;return x;}int mid=l+r>>1;t[x].ls=merge(t[x].ls,t[y].ls,l,mid);t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r);pushup(x);return x;}int query(int x,int l,int r,int L,int R){if(L<=l&&r<=R){//cout<<"query:"<<l<<" "<<r<<"="<<t[x].cnt<<"\n";return t[x].cnt;}int mid=l+r>>1,res=0;if(L<=mid)res+=query(t[x].ls,l,mid,L,R);if(mid<R)res+=query(t[x].rs,mid+1,r,L,R);return res;}
}T;
int n,m;
vector<tuple<int,int> > E[N];
int dis[N],a[N],b[N],ans[N];
void dfs(int x)
{for(auto [y,w] :E[x]){dis[y]=dis[x]+w;dfs(y);}
}
void calc(int x)
{T.insert(T.rt[x],1,n,dis[x]);//cout<<"upd:"<<T.rt[x]<<" "<<dis[x]<<"\n";for(auto [y,w] : E[x]){calc(y);//cout<<"merge:"<<x<<" "<<y<<"="<<T.rt[x]<<" "<<T.rt[y]<<"\n";T.rt[x]=T.merge(T.rt[x],T.rt[y],1,n);}ans[x]=T.query(T.rt[x],1,n,1,a[x]);//cout<<"ans : "<<x<<" : "<<dis[x]<<"="<<ans[x]<<"\n";
}
void work()
{cin>>n>>m;for(int i=1,x,y;i<n;i++){scanf("%lld%lld",&x,&y);E[x].emplace_back(i+1,y);}dfs(1);for(int i=1;i<=n;i++)b[i]=dis[i];b[n+1]=inf;sort(b+1,b+2+n);int tot=unique(b+1,b+2+n)-(b+1);for(int i=1;i<=n;i++){a[i]=dis[i]+m;int k=lower_bound(b+1,b+1+tot,dis[i])-b-1,kk=lower_bound(b+1,b+1+tot,a[i])-b-1;dis[i]= dis[i]==b[k+1] ? k+1 : k;a[i]= a[i]==b[kk+1] ? kk+1 : kk;}calc(1);for(int i=1;i<=n;i++)printf("%lld\n",ans[i]);
}
#undef int
int main()
{//freopen("run.in","r",stdin);//freopen("run.out","w",stdout);work();return 0;
}