bzoj4317 Atm的树 树分治

        一道比较经典的树分治把。。

       二分答案,然后在按照点分治后得到的重心树中找距离<=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

你可能感兴趣的:(二分查找,二分,点分治)