poj 1987 Distance Statistics 点分治

题目大意: 给你一棵树,问两个点对距离<=k的点对个数;


题目分析:点分治裸题。dfs处理出每个重心(分治下)到当前子树根节点的距离存在dis数组中,o(n)直接找出。

但要注意的是,对于dis所存的距离,只适用于两个不同的子树当中,所以要将位于同一个子树中的方案数减去。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
#include<cctype>
#include<cassert>
#include<climits>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define RepD(i,n) for(int i=n;i>=0;i--)
#define MEM(a) memset(a,0,sizeof(a))
#define MEMI(a) memset(a,127,sizeof(a))
#define MEMi(a) memset(a,128,sizeof(a))
#define INF (2139062143)
#define phiF (1000000006)
#define MAXN (1000000+10)
typedef long long LL;


struct info{
     int to,next,val;
     bool use;
}e[100005];
int x,y,z,n,m,tot,first[100005],son[50005],f[50005],size,root,a[50005],dis[50005],k,ans;

void add(int x,int y,int z){
	e[tot].to=y;
	e[tot].val=z;
	e[tot].next=first[x];
	first[x]=tot;tot++;
}


void findroot(int u,int fa){
	son[u]=1;f[u]=0;
	for (int p=first[u];p!=-1;p=e[p].next){
		int v=e[p].to;
		if (v==fa) continue;
		if (e[p].use) continue;
		findroot(v,u);
		f[u]=max(f[u],son[v]);	
		son[u]+=son[v];
	}
	f[u]=max(f[u],size-son[u]);
	if (f[u]<f[root]) root=u;
}

void dfs(int u,int fa){
	son[u]=1;
	a[++a[0]]=dis[u];
	for (int p=first[u];p!=-1;p=e[p].next){
		int v=e[p].to;
		if (!e[p].use&&v!=fa){
			dis[v]=dis[u]+e[p].val;
			dfs(v,u);
			son[u]+=son[v];
		}
	}
	
}


int  count(int u,int val){
	int tmp=a[0]=0;
	dis[u]=val;
	dfs(u,0);
	sort(a+1,a+a[0]+1);
	for (int l=1,r=a[0];l<r;){
		if (a[l]+a[r]<=k){
			tmp+=r-l;l++;
		} else r--;
	}
	return tmp;
	
}

void work(int u){
	ans+=count(u,0);
	for (int p=first[u];p!=-1;p=e[p].next){
		if (!e[p].use){
			e[p^1].use=true;
			ans-=count(e[p].to,e[p].val);
			f[0]=size=son[e[p].to];root=0;
			findroot(e[p].to,0);
			work(root);

		}
	}
	
	
}
char ch;

int main(){
	memset(first,-1,sizeof(first));
	scanf("%d%d",&n,&m);
	For (i,m){
		scanf("%d%d%d %c",&x,&y,&z,&ch);
		add(x,y,z);
		add(y,x,z);
	}
	scanf("%d",&k);
	f[0]=size=n;
	findroot(1,0);
	work(root);
	printf("%d",ans);
	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值