1. 程式人生 > >1017 - 樹分治 - Tree(POJ 1741)

1017 - 樹分治 - Tree(POJ 1741)

傳送門

 

分析

分治是個好東西,樹分治也很妙,其擅長解決樹上路徑一類的問題

在這裡主要是用的點分治(據說比邊分治簡單)

其實其思想不過也就是將一個大問題,不停的分割分割成子問題,然後需要注意(思考)的就是兩個子問題之間產生答案

挪到樹上

我們就分為兩種情況

  1. 處理過重心的路徑
  2. 處理子樹中的路徑

 

事實證明,TLE從來都不是卡常的鍋,你見過哪道題是需要卡常才能A的???

一般TLE都是寫掛了……比如在下

 

程式碼

此題不開long long也是可以的

#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 10009
#define in read()
#define ll long long
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,lim;
int nxt[N*2],to[N*2],head[N],w[N*2],ecnt=0;
void add(int x,int y,int z){nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;w[ecnt]=z;}

int sze[N],fa[N],d[N],dis[N],son[N],G,minn,num=0;
bool vis[N];
ll ans=0;

void dfssize(int u,int fu){//得到子樹大小,及子樹中sze最大的那一個
	sze[u]=1;son[u]=0;
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(vis[v]||(v==fu)) continue;
		dfssize(v,u);
		sze[u]+=sze[v];
		if(sze[v]>son[u]) son[u]=sze[v];
	}
}

void dfsG(int rt,int u,int fu){//尋找當前圖的重心
	if(sze[rt]-sze[u]>son[u]) son[u]=sze[rt]-sze[u];
	if(son[u]<minn) minn=son[u],G=u;
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(vis[v]||(v==fu)) continue;
		dfsG(rt,v,u);
	}
}

void dfsdis(int u,int fu){//尋找每個節點到G的距離
	d[num++]=dis[u];
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(vis[v]||(v==fu)) continue;
		dis[v]=dis[u]+w[e];
		dfsdis(v,u);
	}
}
ll calc(int rt,int L){
	ll res=0;num=0;
	dis[rt]=L;
	dfsdis(rt,0);
	sort(d,d+num);
	int l=0,r=num-1;
	while(l<r){
		if(d[l]+d[r]<=lim) {
			res+=r-l;//////////不能算自己
			l++;
		}
		else r--;
	}
	return res;
}
void solve(int u){
	minn=n;
	dfssize(u,0);
	dfsG(u,u,0);//以 u 為根的子樹,尋找當前的重心 
	vis[G]=1;
	ans+=calc(G,0);
	for(int i=head[G];i;i=nxt[i]){
		if(!vis[to[i]]) {
			ans-=calc(to[i],w[i]);
			solve(to[i]);
		}
	}	
}
int main(){
	while(1){
		n=in;lim=in;
		if(!n&&!lim) break;
		int i,j,k;
		num=ecnt=0;
		for(i=1;i<=n;++i) vis[i]=0,head[i]=0;
		for(i=1;i<n;++i){
			int u=in,v=in,z=in;
			add(u,v,z);add(v,u,z);
		}
		ans=0;
		solve(1);
		printf("%lld\n",ans);		
	}
	return 0;
}