一道比较经典的树分治把。。
二分答案,然后在按照点分治后得到的重心树中找距离<=d的点的数量即可。时间复杂度O(Nlog^3N)。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define inf 1000000000 #define N 30005 #define M 1200005 using namespace std; int n,k,m,cnt,rt,tot=1,all,pnt[M],edg[M],len[M],nxt[M]; int a[M],f[N],sz[N],pl[N],pr[N],ql[N],qr[N]; struct graph{ int fst[N]; void add(int x,int y,int z,int w){ pnt[++tot]=y; edg[tot]=z; len[tot]=w; nxt[tot]=fst[x]; fst[x]=tot; } }g1,g2; void get_rt(int x,int fa){ int p; sz[x]=1; f[x]=0; for (p=g1.fst[x]; p; p=nxt[p]) if (edg[p]){ int y=pnt[p]; if (y!=fa){ get_rt(y,x); sz[x]+=sz[y]; f[x]=max(f[x],sz[y]); } } f[x]=max(f[x],all-sz[x]); if (f[x]<f[rt]) rt=x; } void dfs(int x,int fa,int dep){ int p; a[++m]=dep; for (p=g1.fst[x]; p; p=nxt[p]) if (edg[p]){ int y=pnt[p]; if (y!=fa) dfs(y,x,dep+len[p]); } } void build(int x,int fa,int dep){ g2.add(x,rt,cnt,dep); a[++m]=dep; int p; for (p=g1.fst[x]; p; p=nxt[p]) if (edg[p]){ int y=pnt[p]; if (y!=fa) build(y,x,dep+len[p]); } } void solve(int x){ pl[x]=m+1; dfs(x,0,0); pr[x]=m; sort(a+pl[x],a+pr[x]+1); int p; for (p=g1.fst[x]; p; p=nxt[p]) if (edg[p]){ int y=pnt[p]; ql[++cnt]=m+1; build(y,x,len[p]); qr[cnt]=m; sort(a+ql[cnt],a+qr[cnt]+1); } for (p=g1.fst[x]; p; p=nxt[p]) if (edg[p]){ edg[p]=edg[p^1]=0; int y=pnt[p]; all=sz[y]; rt=0; get_rt(y,x); solve(rt); } } int find(int x,int y,int z){ int l=x,r=y+1,mid; if (z<a[x]) return 0; while (l+1<r){ mid=(l+r)>>1; if (a[mid]<=z) l=mid; else r=mid; } return l-x+1; } int check(int x,int y){ int p,tmp=find(pl[x],pr[x],y); for (p=g2.fst[x]; p; p=nxt[p]){ int u=pnt[p],v=edg[p]; tmp+=find(pl[u],pr[u],y-len[p])-find(ql[v],qr[v],y-len[p]); } return tmp; } int main(){ scanf("%d%d",&n,&k); k++; int i,x,y,z; for (i=1; i<n; i++){ scanf("%d%d%d",&x,&y,&z); g1.add(x,y,1,z); g1.add(y,x,1,z); } f[0]=inf; rt=0; all=n; get_rt(1,0); solve(rt); int l,r,mid; for (i=1; i<=n; i++){ l=0; r=10*(n-1); while (l<r){ mid=(l+r)>>1; if (check(i,mid)<k) l=mid+1; else r=mid; } printf("%d\n",l); } return 0; }
by lych
2016.3.31