1017 - 树分治 - Tree(POJ 1741)

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/83119704

传送门

分析

分治是个好东西,树分治也很妙,其擅长解决树上路径一类的问题

在这里主要是用的点分治(据说比边分治简单)

其实其思想不过也就是将一个大问题,不停的分割分割成子问题,然后需要注意(思考)的就是两个子问题之间产生答案

挪到树上

我们就分为两种情况

  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;
}

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/83119704
今日推荐