树分治(点分治)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38944163/article/details/81544134

点分治主要能解决的问题是树上路径个数的问题。

分治也并没有想象中的那么难,就是时间复杂度有点玄学,有的人说是nlog^2n有的说是nlogn,我也不知道具体是多少(反正比n^2暴力快就是了)

点分治的思想就是每次取一个重心,然后把每个经过该重心的路径数量算出来(要用容斥,注意重心是否要被计算),对于重心的每个儿子都递归进去做同样的操作,最后累加即可(看代码就懂了)。

扔道题

题意就是求长度不超过k的路径的数量总数。

代码:

#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200005;
struct A{
	int v,c,next;
}e[N];
int p[N],q[N],dep[N],size[N],msize[N],vis[N],n,K,eid,l,r;
void init(){
	memset(p,-1,sizeof(p));
	memset(vis,0,sizeof(vis));
	l=r=0;
	eid=0;
}
void add(int u,int v,int c){
	eid++;
	e[eid].v=v;
	e[eid].c=c;
	e[eid].next=p[u];
	p[u]=eid;
}
void dfs(int x){
	q[++r]=x;
	vis[x]=1;size[x]=1;msize[x]=0;
	for(int i=p[x];i+1;i=e[i].next){
		int v=e[i].v;
		int c=e[i].c;
		if(vis[v]) continue;
		dep[v]=dep[x]+c;
		dfs(v);
		size[x]+=size[v];
		msize[x]=max(msize[x],size[v]);
	}
	vis[x]=0;
}
bool cmp(int x,int y){
	return dep[x]<dep[y];
}
int calc(int x,int y){//计算
	int ans=0;
	sort(q+x,q+1+y,cmp);
	for(int i=x,j=y;i<j;i++){
		while(j>i&&dep[q[i]]+dep[q[j]]>K) j--;
		ans+=j-i;
	}
	return ans;
}
int work(int x){ //主题
	l=1,r=0;
	dfs(x);
	int top=size[x],mi=msize[x];
	for(int i=1;i<=r;i++){//找重心(保证log级)
		int v=q[i];
		if(max(msize[v],top-size[v])<mi) mi=max(msize[v],top-size[v]),x=v;
	}
	vis[x]=1;
	int ans=0;
	l=1;r=0;
	for(int i=p[x];i+1;i=e[i].next){
		int v=e[i].v;
		int c=e[i].c;
		if(vis[v]) continue;
		dep[v]=c; dfs(v);
		ans-=calc(l,r);//容斥
		l=r+1;
	}
	dep[x]=0;q[++r]=x;
	ans+=calc(1,r);
	for(int i=p[x];i+1;i=e[i].next){
		int v=e[i].v;
		if(vis[v]) continue;
		ans+=work(v);
	}
	return ans;
}
int main(){
	for(;;){
		init();
		scanf("%d%d",&n,&K);
		if(n==0&&K==0) return 0;
		for(int i=1;i<n;i++){
			int u,v,c;
			scanf("%d%d%d",&u,&v,&c);
			add(u,v,c);
			add(v,u,c);
		}
		printf("%d\n",work(1));
	}
	
	return 0;
}

 

展开阅读全文

没有更多推荐了,返回首页