POJ 1741 点分治

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/getsum/article/details/88539429

题目大意:给出一棵有n个节点的树和树中每条边的权值,再给出一个数k,求出树中所有长度小于等于k的路径条数。

点分治模板题,主要思路是:

1.找到树的重心root,将其当作根。

2.对于以root为根的树,一共有两种路径:一种路径过root,一种不过root,即全部在root的一个子树里面。不过由于我们在后面会将树“分治”,所以第二种情况在以后被分治的树中也会变成第一种情况,所以后面只讨论第一种情况。

3.从root开始往子树dfs,用dis[i]表示i节点到root的长度。然后从小到大对dis排序,利用双指针便可以计算出以root为根的子树的所有dis[i]+dis[j]<=k的所有路径条数。

4.不过仔细想想这个做法其实有问题,思考如下的状况:

假如dis[3]+dis[4]>k,那么这一对便不会其中,但是实际上3号点和4号点的距离应该是dis[3]+dis[4]-dis[2],因此上面的算法会漏掉一些可能的解。

这种情况即为上面说的“第二种情况”,由于我们进行分治的时候会讨论到后面的子树,比如我们讨论到了以2为根节点的子树,那么3号节点和4号节点的问题就从“第二种情况”变为“第一种情况”。因此我们只需要把这种情况直接减去即可

5.返回到1,进行分治。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;
const int maxn=1e4+5;
const int inf=0x3f3f3f3f;
int dis[maxn],n,k,maxson[maxn],root,minn,cntson[maxn],s,tot;
LL ans;
bool vis[maxn];
struct Edge{
	int u,v,d;
	Edge(int u,int v,int d):u(u),v(v),d(d){}
};
vector<int>G[maxn];
vector<Edge>edges;
void add_edge(int u,int v,int d){
	edges.push_back(Edge(u,v,d));
	G[u].push_back(edges.size()-1);
}
void getroot(int u,int fa){
	cntson[u]=1;maxson[u]=0;
	for(int i=0;i<G[u].size();i++){
		Edge e=edges[G[u][i]];
		if(fa==e.v||vis[e.v])continue;
		getroot(e.v,u);
		cntson[u]+=cntson[e.v];
		maxson[u]=max(maxson[u],cntson[e.v]);
	}
	maxson[u]=max(maxson[u],s-maxson[u]);
	if(maxson[u]<minn)root=u,minn=maxson[u];
}
void getdis(int u,int fa,int len){
	dis[++tot]=len;
	for(int i=0;i<G[u].size();i++){
		Edge e=edges[G[u][i]];
		if(vis[e.v]||fa==e.v)continue;
		getdis(e.v,u,len+e.d);
	}
}
int cacul(int u,int del){
	tot=0;
	memset(dis,0,sizeof(dis));
	getdis(u,0,del);
	sort(dis+1,dis+tot+1);
	int l=1,r=tot,cur=0;
	while(l<=r)
		if(dis[l]+dis[r]<=k)cur+=r-l,l++;
		else r--;//线性计算出满足<=k的路径条数
	return cur;
}
void div(int u){
	ans+=cacul(u,0);//将所有情况加上
	vis[u]=1;
	for(int i=0;i<G[u].size();i++){
		Edge e=edges[G[u][i]];
		if(vis[e.v])continue;
		ans-=cacul(e.v,e.d);
//减去所有情况,由于以e.v为根的子树没有e.d这条路,所以在之前附加这个长度
		s=cntson[e.v],minn=inf;
		getroot(e.v,0);
		div(root);
	}
}
int main(){
	while(scanf("%d%d",&n,&k)){
		if(n==0&&k==0)return 0;
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=n;i++)G[i].clear();
		for(int i=0;i<edges.size();i++)edges.clear();
		int x,y,z;
		for(int i=1;i<n;i++){
			scanf("%d%d%d",&x,&y,&z);
			add_edge(x,y,z);
			add_edge(y,x,z);
		}
		s=n;
		ans=0,minn=inf;
		getroot(1,0);
		div(root);
		cout<<ans<<endl;
	}
}

猜你喜欢

转载自blog.csdn.net/getsum/article/details/88539429