洛谷4178 BZOJ1468 Tree题解点分治
点分治的入门练习。
BZOJ的链接(权限题)
关于点分治的思想我就不再重复了,这里重点说一下如何判重。
我们来看上图,假设我们去除了1节点,求出d[2]=1,d[3]=d[4]=2
假设k为5,这样我们会认为节点(2,3)(2,4)(3,4)的距离小于k,从而累计到答案中
但是我们以2为root做点分治时还会将(3,4)计算一遍,这样就重复了
所以我们每一次计算答案时还要讲所有多余情况减去,最终答案才是我们要求的答案
不难发现多余情况是在root节点与root子节点重复统计的,我们在点分治时将所有root子节点的答案减去就好了
# include<iostream> # include<cstdio> # include<algorithm> # include<set> # include<cmath> using namespace std; const int mn = 40005; struct edge{int to,next,dis;}; edge e[mn*2]; int head[mn],edge_max; void add(int x,int y,int z) { e[++edge_max].to=y; e[edge_max].dis=z; e[edge_max].next=head[x]; head[x]=edge_max; } bool vis[mn]; int n,k,ans; int mx[mn],siz[mn],root,sum; int d[mn],deep[mn]; void getroot(int x,int fa) { siz[x]=1,mx[x]=0; for(int i=head[x];i;i=e[i].next) { int u=e[i].to; if(u==fa || vis[u]) continue; getroot(u,x); siz[x]+=siz[u]; mx[x]=max(mx[x],siz[u]); } mx[x]=max(mx[x],sum-siz[x]); if(mx[root]>mx[x]) root=x; } void getdeep(int x,int fa) { deep[++deep[0]]=d[x]; for(int i=head[x];i;i=e[i].next) { if(e[i].to==fa || vis[e[i].to]) continue; d[e[i].to]=d[x]+e[i].dis; getdeep(e[i].to,x); } } int cal(int x,int now) { d[x]=now,deep[0]=0; getdeep(x,0); sort(deep+1,deep+1+deep[0]); int t=0,l,r; for(int l=1,r=deep[0];l<r;) { if(deep[l]+deep[r]<=k) { t+=r-l; l++; } else r--; } return t; } void solve(int x) { vis[x]=1; ans+=cal(x,0); for(int i=head[x];i;i=e[i].next) { if(vis[e[i].to]) continue; ans-=cal(e[i].to,e[i].dis); sum=siz[e[i].to]; root=0; getroot(e[i].to,root); solve(root); } } int main() { int x,y,z; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); } scanf("%d",&k); mx[0]=1<<30,sum=n; getroot(1,0); solve(root); printf("%d\n",ans); return 0; }