树分治基础模板以及树的重心(poj1741 tree)

好久没有更新博文了,这里更新一发~~

Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.

Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.

Write a program that will count how many pairs which are valid for a given tree.

Input

The input contains several test cases. The first line of each
test case contains two integers n, k. (n<=10000) The following n-1
lines each contains three integers u,v,l, which means there is an edge
between node u and v of length l.

The last test case is followed by two zeros.

Output

For each test case output the answer on a single line.

Sample Input

5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

Sample Output

8简单翻译一下,给定你若干条边,然后让你求任意两点之间的距离小于等于k的有多少对,暴力n^2肯定要炸,于是我们就可以想到用树分治。我们发现总共只有2种情况,经过根节点和不经过根节点。设d[i]为i节点到根节点的距离(注意:根节点是会变动的,因为我们每一次都要求一次重心,为了不使树退化成一条链,于是根节点就是重心节点是变动的)d[i]+d[j]<=k就是表示经过根节点,我们发现排序之后d[i]是有序的,于是我们用两个指针在序列中进行搜索就可以了这里复杂度为on然后求剩下不经过根节点的情况的时候我们就在他自身的子节点中同样进行上述的操作就可以了,注意要求重心!!然后综合一下,此题使用树的分治算法时间复杂度为O(nlog^2n) 。 接下来放上一份我抄过来的代码,明天应该还要再写一遍的~这代码感觉思路比较的清晰,我做了一些改动。
#pragma GCC optimize("O2")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 20000
using namespace std;
int s[maxn],f[maxn],d[maxn],book[maxn],n,k,root,ans,size;

struct Node{int to,w;};
vector<Node> g[maxn];
vector<int> dep;

int getroot(int x,int fa)
{
	int u;
	s[x]=1,f[x]=0;
	for(int i=0;i<g[x].size();i++)
	{
		u=g[x][i].to;
		if(u!=fa&&!book[u])
		{
			getroot(u,x);
			s[x]+=s[u];
			f[x]=max(f[x],s[u]);
		}
	}
	f[x]=max(f[x],size-s[x]);
	if(f[x]<f[root]) root=x;
}

void getdep(int x,int fa)
{
	dep.push_back(d[x]);s[x]=1;
	for(int i=0;i<g[x].size();i++)
	{
		int u=g[x][i].to;
		if(u==fa||book[u]) continue;
		d[u]=d[x]+g[x][i].w;
		getdep(u,x);
		s[x]+=s[u];
	}
}

int calc(int x,int init)
{
	dep.clear(),d[x]=init;
	getdep(x,0);
	int ans=0;
	sort(dep.begin(),dep.end());
	for(int l=0,r=dep.size()-1;l<r;)//这里细节要注意
		if(dep[l]+dep[r]<=k) ans+=r-l,l++;
		else r--;
	return ans;
}

void work(int x)
{
	ans+=calc(x,0),book[x]=1;
	for(int i=0;i<g[x].size();i++)
	{
		int u=g[x][i].to;
		if(book[u]) continue;
		ans-=calc(u,g[x][i].w);
		f[0]=size=s[u];
		getroot(u,root=0);
		work(root);
	}
}

int main()
{
	while(cin>>n>>k&&n&&k)
	{
		memset(book,0,sizeof(book));
		for(int i=1;i<=n;i++) g[i].clear();
		for(int i=1;i<n;i++)
		{
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			g[u].push_back((Node){v,w});
			g[v].push_back((Node){u,w});
		}
		f[0]=size=n;
		getroot(1,root=0);
		ans=0;
		work(root);
		printf("%d\n",ans);
	}
	return 0;
}

也就90行不算多~up++

				
时间: 2024-10-10 18:17:51

树分治基础模板以及树的重心(poj1741 tree)的相关文章

线段树分治总结(线段树分治,线段树,并查集,树的dfn序,二分图染色)

闲话 stO猫锟学长,满脑子神仙DS 线段树分治思想 我们在做CDQ的时候,将询问和操作通通视为元素,在归并过程中统计左边的操作对右边的询问的贡献. 而在线段树分治中,询问被固定了.按时间轴确定好询问的序列以后,我们还需要所有的操作都会影响一个时间区间.而这个区间,毫无疑问正好对应着询问的一段区间. 于是,我们可以将每一个操作丢到若干询问里做区间修改了,而线段树可以高效地维护.我们开一个叶子节点下标为询问排列的线段树,作为分治过程的底层结构. 具体的实现,仍然要看题目. 例题1 BZOJ4025

bzoj 4137 [FJOI2015]火星商店问题——线段树分治+可持久化01trie树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4137 关于可持久化01trie树:https://www.cnblogs.com/LadyLex/p/7281110.html 看了看它的两道例题,就没写. 特殊商品可以直接用可持久化trie做. 其他部分用线段树分治.修改是单点的,询问是区间,原来想的是把询问区间定位后有 mlogn 个,在线段树的每个叶子上贡献一番:结果TLE了,因为若是在叶子处贡献,一个询问就要做 r-l+1 次.

线段树(SegmentTree)基础模板

线段树模板题来源:https://www.lintcode.com/problem/segment-tree-build/description 201.?线段树的构造 /** * Definition of SegmentTreeNode: * class SegmentTreeNode { * public: * int start, end; * SegmentTreeNode *left, *right; * SegmentTreeNode(int start, int end) { *

【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态树分治

题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来.在游戏中,幽香可能在空地上增加或者减少一些军队.同时,幽香可以在一个空地上放置一个补给站. 如果补给站在点u上,并

HDU5469 Antonidas(树分治&amp;&amp;哈希)

给你一颗点上有字符的树,问一个给定的字符串是否是这棵树上的两点的路径. 树分治的思想就是每次找重心,重心下的子问题分解去做,然后就是合并了.合并的时候用一个总的set<pair<len,hash>> 去存从根节点往下走的长度以及对应的hash值,判的时候只需要看下是否已经存在 m-len,以及对应的前缀(或者后缀)的哈希值,然后再加进来. 两个优化的点是,1是递归解子问题的时候如果子树规模小于要给的字符串可以不用递归下去.2是存pair的时候只需要存前缀的pair以及后缀的pair

树分治 复习总结

前几天去填重建计划的坑的时候无意中发现自己的树分治一直有一个地方有点bug,是错误的QAQ(但是貌似影响不大 于是皇德耀世,赶紧去找了几道树分治来练习一下 bug是每次树分治找重心的时候sum直接用的w数组,可是对于每一层w数组要重新计算(然而我并没有QAQ WC2010 重建计划 这是一份bug重重的代码,我用了19min写完,交上去1A 可是我后来发现这份代码至少有三处错误QAQ数据也太弱了吧 贴一份bug重重的代码吧,顺便锻炼找bug能力? 做法是二分答案之后每条边-mid,判断条件是是否

hdu-5977 Garden of Eden(树分治)

题目链接: Garden of Eden Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 210    Accepted Submission(s): 75 Problem Description When God made the first man, he put him on a beautiful garden, the G

BZOJ 1036: [ZJOI2008]树的统计Count 【树链剖分】

Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入的第一行为一个整数n,表示节点的个数.接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条

POJ 1741 树分治(点分治模板题)

POJ 1741 题意:求一棵树中点对<=k的数量. 总结:点分治,搞不太懂..大概敲了一遍 #include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stac