点分治 模板

poj 1741

1.选取一个点,把无根树变成有根树。通过树形dp的方式选择。用son记录点的子树大小,用F算出最大的子树,当F[x]<F[root]时,更换root.

2.再次通过dfs处理树上的deep和距离。

3.处理连通块中通过根节点的路径。减去同一子树内部的路径。

4.递归。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>

using namespace std;
typedef long long ll;
const int maxn = 10000+5;
const int maxm = 20000+10;
bool vis[maxn];
int head[maxn];
int nxt[maxm],w[maxm],e[maxm],n,k,root,sum,x,y,z;
int ans;
int F[maxn];//sum of son
int son[maxn];
int dep[maxn];
int d[maxn];

void getroot(int x,int fa){
	son[x]=1;F[x]=0;
	for(int k=head[x];k!=-1;k=nxt[k]){
		if(e[k]==fa||vis[e[k]]) continue;
		getroot(e[k],x);
		son[x]+=son[e[k]];
		F[x]=max(F[x],son[x]);
	}
	F[x]=max(F[x],sum-F[x]);
	if(F[x]<F[root]) root=x;
}

void getdep(int x,int fa){
	dep[++dep[0]]=d[x];
	for(int k=head[x];k!=-1;k=nxt[k]){
		if(e[k]==fa||vis[e[k]]) continue;
		d[e[k]]=d[x]+w[k];
		getdep(e[k],x);
	}
}

int calc(int x,int v){
	d[x]=v;dep[0]=0;
	getdep(x,0);
	sort(dep+1,dep+1+dep[0]);
	int l=1,r=dep[0],ret=0;
	while(l<r){
		if(dep[r]+dep[l]<=k){
			ret+=r-l;
			l++;
		} else {
			r--;
		}
	}
	return ret;
}

void solve(int x){
	ans+=calc(x,0);
	vis[x]=1;
	for(int k=head[x];k!=-1;k=nxt[k]){
		if(vis[e[k]]) continue;
		ans-=calc(e[k],w[k]);
		sum=son[e[k]];
		root=0;
		getroot(e[k],0);
		solve(root);
	}
}

int main(){
	//freopen("in.txt","r",stdin);
	while(~scanf("%d%d",&n,&k)){
		if(!n&&!k) break;
		ans=root=0;
		memset(vis,0,sizeof vis);
		memset(head,-1,sizeof head);
		for(int i=1;i<n;i++){
			scanf("%d%d%d",&x,&y,&z);
			e[i]=y;w[i]=z;nxt[i]=head[x];head[x]=i;
			e[i+n]=x;w[i+n]=z;nxt[i+n]=head[y];head[y]=i+n;
		}
		F[0]=INT_MAX;sum=n;
		getroot(1,0);
		solve(root);
		printf("%d
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/foreignbill/p/7821244.html