`

poj1741(树的分治,基于边的 分治)

阅读更多

/* 
树基于边的分治算法,计算树中距离小于等于k的点对数目
点的下标从1开始,树里实际的节点个数的上界是N*3
当点只有一个的时候,得到的是空树(这个需要注意),
分治树有O(log(NUM))层,如果会动态改变树里的信息的话,
还需要记录每个的节点在每一层是在左子树还是右子树,以及在这一层
到根的距离信息(这里的根指的是这一层的分治边的两个点中到该点距离
较近的那个点)。
树重构后[1,n]为原树中的 节点,[n+1,cnt]为虚拟节点
*/ 
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
const int N = 10005;
const int NUM=N*3; //重构后树中的节点的个数最大值
typedef int wtype;  //边权的类型
const wtype INF = 0X7FFFFFFF;
struct e{
	int u, v;
	wtype len;
	e *nxt, *pair;
	bool used;  //标记一条边是否已经被选过了
	//分治树的数据结构
	e *ls, *rs;
}*fir[NUM], es[(NUM+N)*2];
int n, en, cnt, k; //cnt为重构后的树中的节点
int que[NUM], par[NUM], size[NUM];
e* eque[NUM];
wtype len[NUM];
wtype lens[2][NUM];
int nums[2], ans;
void clear(int n){
	en = 0;
	int i;
	for(i = 1; i <= n; i++){
		fir[i] = NULL;
	}
}
void add_e(int u, int v, e* pair, wtype len){
	es[en].u = u;
	es[en].v = v;
	es[en].pair = pair;
	es[en].len = len;
	es[en].used = false;
	es[en].nxt = fir[u];
	fir[u] = &es[en++];
}
void insert(int u, int v, wtype len){
	add_e(u, v, &es[en+1], len);
	add_e(v, u, &es[en-1], len);
}
void adjust(int l, int r, int f, int& cnt){
	if(r-l <= 1){
		while(l <= r){
			int u = que[l];
			par[u] = f;
			insert(u, f, eque[l++]->len);
		}
	}else{
		int mid = (l+r)>>1, ls, rs;
		fir[ls=++cnt] = NULL; insert(ls, f, 0);
		fir[rs=++cnt] = NULL; insert(rs, f, 0);
		par[ls]=par[rs]=f;
		adjust(l, mid, ls, cnt);
		adjust(mid+1, r, rs, cnt);
	}
}
void adjust(int n){ //重构树
	int l, r, u, v, mid;
	e* cur;
	cnt = n;
	for(l=r=0, par[1]=-1, que[r++]=1; l!=r; ){
		for(cur = fir[u=que[l++]], mid=r; cur; cur = cur->nxt){
			if(!cur->used && (v=cur->v) != par[u]){
				que[r] = v;
				par[v] = u;
				eque[r++] = cur;
			}
		}
		if(r-mid > 2){
			adjust(mid, r-1, u, cnt);
			//删边,只需把那些边的used标记为true即可
			while(mid<r){
				eque[mid]->used=true;
				eque[mid++]->pair->used=true;
			}
		}
	}
}
inline int ABS(int a){
	return a>0 ? a:-a;
}
e* getRoot(int s, bool isLeft){  //找根节点
	e *ans=NULL, *cur;
	int l, r, u, num, v, ds, i;
	nums[isLeft]=0;
	for(l=r=0, par[s]=-1, len[s]=0, que[r++]=s; l!=r; ){
		for(cur=fir[u=que[l++]]; cur; cur=cur->nxt){
			if(!cur->used && (v=cur->v) != par[u]){
				eque[r]=cur;
				que[r++]=v;
				par[v]=u;
				len[v]=len[u]+cur->len;
			}
		}
	}
	for(i=r-1; i>=0; i--){
		if(que[i] <= n){
			lens[isLeft][nums[isLeft]++]=len[que[i]];
		}
	}
	for(num=r, i=r-1; i>=1; i--){//队列里的第一个点不可能被选择
		u=que[i]; size[u]=1;
		for(cur=fir[u]; cur; cur=cur->nxt){
			if(!cur->used && (v=cur->v) != par[u]){
				size[u] += size[v];
			}
		}
		if(ans==NULL || ds > ABS(num-size[u]*2)){
			ans=eque[i];
			ds=ABS(num-size[u]*2);
		}
	}
	return ans;
}
void division(e*& r, int d){
	if(!r) return ;
	//r->u作为左孩子,r->v作为右孩子
	r->used = true; r->pair->used = true;
	r->ls = getRoot(r->u, true);
	r->rs = getRoot(r->v, false);
	int il, ir;
	//计算点对
	sort(lens[0], lens[0]+nums[0]);
	sort(lens[1], lens[1]+nums[1]);
	for(il=0, ir=nums[0]-1; il<nums[1]; il++){
		while(ir >= 0 && lens[1][il]+lens[0][ir]+r->len>k) ir--;
		ans += ir+1;
	}
	division(r->ls, d+1);
	division(r->rs, d+1);
}
//r为根节点(初始为空,s为一个起点,n为点的个数)
void build(e*& r, int s, int n){ //构建分治树
	adjust(n);  //首先得调整树的结构
	r = getRoot(s, false);
	division(r, 1);
}

//输入一个整数
template<typename T>
void getSNum(T& ans){
	char ch;
	int s;
	while(true){
		ch = getchar();
		if((ch >= '0' && ch <= '9') || ch == '-') break;
	}
	if(ch == '-'){
		s = -1;
		ans = 0;
	}else{
		s = 1;
		ans = ch -'0';
	}
	while(true){
		ch = getchar();
		if(!(ch >= '0' && ch <= '9')) break;
		ans = ans*10+ch-'0';
	}
	ans *= s;
}
bool input(){
	scanf("%d%d", &n, &k);
	if(n == 0 && k == 0) return false;
	int u, v, i;
	wtype len;
	clear(n);
	for(i = 1; i < n; i++){
		getSNum(u);
		getSNum(v);
		getSNum(len);
		insert(u, v, len);
	}
	return true;
}
void solve(){
	e* root=NULL;
	ans=0;
	if(n > 1){
		build(root, 1, n);
	}
	printf("%d\n", ans);
}
int main(){
	//freopen("in.txt", "r", stdin);
	while(input()) solve();
	return 0;
}
 
分享到:
评论

相关推荐

    C_(POJ_1854)(分治).cpp

    C_(POJ_1854)(分治).cpp

    NOIP NOI 信息学竞赛 ACM-ICPC POJ(北京大学在线评测系统)刷题推荐 OI复习计划 算法大纲

    (poj1753,poj2965)(2)贪心(poj1328,poj2109,poj2586)(3)递归和分治法.(4)递推.(5)构造法.(poj3295)……中级有:(1)C++的标准模版库的应用. (poj3096,poj3007)(2)较为复杂的模拟题的训练(poj3393,poj1472,poj3371,...

    raid.rar_Raid!_两组点最近点

    POJ 2659 Raid 分治法解决两组点中距离最近的点对

    北大oj 题目分类

    (3)递归和分治法. (4)递推. (5)构造法.(poj3295) (6)模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996) 二.图算法: (1)图的深度优先遍历和广度优先遍历. (2)最短路径算法(dijkstra,bellman-ford,floyd,...

    poj-solve:算法练习

    Algorithm-Java Algorithms + Data Structures = Programs....最短路径(dijkstra,bellman-ford,floyd,heap+dijkstra)(,,poj1062,poj2253,poj1125,poj2240) 最小生成树(prim,kruskal)(p

    程序员必须掌握哪些算法_介绍

    一.基本算法:枚举. (poj1753,poj2965)贪心(poj1328,poj2109,poj2586)递归和分治法

    cPP.zip_二部图

    关于二部图、线段数、树状数组、树的分治的一些POJ题目的解答

    挑战程序设计竞赛(第2版)

    4.6.2 树上的分治法 4.6.3 平面上的分治法 4.7 华丽地处理字符串 4.7.1 字符串上的动态规划算法 4.7.2 字符串匹配 4.7.3 后缀数组 4.8 一起来挑战GCJ的题目(3) 4.8.1 Mine Layer 4.8.2 Year of More Code Jam ...

    算法实验(二分+中位数-9-11题)1

    分治+中位数第 9 题:poj1723 SOLDIERSN soldiers of the land Gridland are randomly scatter

    实验-8题1

    1. 最近点对问题的算法实现 2. 利用分治法设计一个计算两个 n 位的大整数相乘的算法,要求计算时间低于 O(n2) 5. Poj1065 Wooden St

    cpp-算法精粹

    这样的一大好处是,读者可以边看书,边实现自己的代码,然后提交到网站上验证自己的想法是否正确。AlgoHub的使命是成为最好的算法学习和交流平台。AlgoHub囊括了 POJ, ZOJ, leetcode, HackerRank 等网站的经典题目...

    封装求大整数的相关操作

    在poj上accepted的代码。对大整数进行了封装,能很方便地求而出1000位以内的整数的运算。

    kuangbin acm模板超级好用

    2.6 随机素数测试和大数分解 (POJ 1811) . . . . . . . . . . . . . . . . . . . . . . . 29 2.7 欧拉函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.7.1 分解质因素求...

    OJ_Practice

    刷题使人进步 :grinning_face_with_smiling_eyes: 为什么刷题 希望将来项目提升自己的编程能力。 有些比较难懂难刷的译文,除了写基本的代码外,我将会以markdown的形式...分治 数据结构 佐治 简单题 搜寻 POJ 动态规划

Global site tag (gtag.js) - Google Analytics