P4178 Tree
P4178 Tree
题目描述:
给定一棵 n 个节点的树,每条边有边权,求出树上两点距离小于等于 k 的点对数量。
数据范围:
说句闲话
感谢著名CB大师red_fire倾情推荐%%%
在机房三个人唇枪舌剑了一小会,我们的CB大师直接开搞线段树,而仔细研读了数据范围的本蒟蒻认为sort十分的清真,于是就有了这场对决
Solution:
首先我们不难想到像这样
“树上两点距离小于等于 k的点对数量。”
的题显然可以用淀粉质解决
废话今天可是点分治专题啊:(
我们考虑如何统计答案:对于一个点cent:把它的所有儿子v到cent的dis全部求出来并记录到一个数组A中,然后对A排序,排序后在上面跑一个双指针,求出对于每个l,从右往左数第一个满足
注意,尽管区间长度是r-l+1,但是[l,l]并不是一个点对,所以不能将[l,l]统计进答案
但是我们会发现这样写的话同一个y内的点对也有可能被统计进答案,我们只需要对于x的每一个儿子y,去一下y内的重就好了。
Code:
#include<bits/stdc++.h> #define int long long const int N=4e4+5; using namespace std; long long ans; int n,m,e_cnt,k; int head[N<<1],nxt[N<<1],to[N<<1],w[N<<1]; void add(int x,int y,int z) { e_cnt++; to[e_cnt]=y;nxt[e_cnt]=head[x];w[e_cnt]=z; head[x]=e_cnt; } int tot,cent; int vis[N],siz[N],mx[N],A[N],dis[N]; void get_cent(int x,int fa) { siz[x]=1;mx[x]=0; for(int i=head[x],y;i;i=nxt[i]) { y=to[i]; if(y==fa||vis[y])continue; get_cent(y,x); siz[x]+=siz[y]; mx[x] = mx[x] > siz[y] ? mx[x] : siz[y]; } mx[x] = mx[x] > tot-siz[x] ? mx[x] : tot-siz[x]; cent = mx[cent] < mx[x] ? cent : x; } void get_ans(bool tag) { sort(A+1,A+1+A[0]); for(int l=1,r=A[0];l<=A[0];l++) { while(A[l]+A[r]>k)r--; if(l<r) ans+= (tag ? l-r : r-l); if(r<l)break; } A[0]=0; } inline void get_dis(int x,int fa) { A[++A[0]]=dis[x]; for(int i=head[x],y;i;i=nxt[i]) { y=to[i]; if(y==fa||vis[y])continue; dis[y]=dis[x]+w[i]; get_dis(y,x); } } void calc(int x) { dis[x]=0; get_dis(x,0); vis[x]=1; get_ans(0); for(int i=head[x],y;i;i=nxt[i]) { y=to[i]; if(vis[y])continue; dis[y]=dis[x]+w[i]; get_dis(y,x); get_ans(1); } } void solve(int x) { calc(x); for(int i=head[x],y;i;i=nxt[i]) { y=to[i]; if(vis[y])continue; cent=0;tot=siz[y]; get_cent(y,x); solve(cent); } } void work() { cin>>n; for(int i=1,x,y,z;i<n;i++) { scanf("%lld%lld%lld",&x,&y,&z); add(x,y,z); add(y,x,z); } cin>>k; mx[cent=0]=n; tot=n; get_cent(1,0); solve(cent); printf("%lld",ans); } #undef int int main() { //freopen("P4178_1.in","r",stdin); //freopen("Tree.out","w",stdout); work(); return 0; }