【BZOJ】1468: Tree(点分治)

http://www.lydsy.com/JudgeOnline/problem.php?id=1468

分治真是一门高大上的东西。。。

好神。。。

树分治最好资料是:qzc的《分治算法在树的路径问题中的应用》

我来说说自己的理解:

点分=找重心+分治

找重心尤为重要,因为这关系到时间复杂度。

对于递归式

$$T(n)=aT(n/b)+O(D(n))$$

这类递归式,如果能保证每一层都是$O(D(n))$,那么时间复杂度会大大减小。(详见算导第三章和第四章)

对于一棵树,如果我们在找到重心后,用线性做法处理完当前层,那么就可以log级别处理完整棵树,比如:

递归式

$$T(n)=aT(n/a)+O(n)$$

就是非常一般的点分的分治,在这里复杂度为$O(nlgn)$,具体证明看算导。。(很简单的。。。。。

因此如果我们能线性时间内处理好每一层,问题就能在nlgn的时间内得以解决。。。。。

本题裸的路径询问。。。。。。。。。。。。。。。。。。。。。。。。。。。对于此类问题,考虑经过每个点的路径。。。。

预处理当前根所有子树的信息,然后加上根然后合并就能得到一条经过根的路径!然后就行了。。。。。随便搞搞就能线性了。。

本题只要将当前根所有子树节点到根的距离全部跑出来,排序后因为单调维护一下即可。。。。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define error(x) (!(x)?puts("error"):0)
#define rdm(x, i) for(int i=ihead[x]; i; i=e[i].next)
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }

const int N=40005, oo=~0u>>1;
int ihead[N], cnt, K;
struct dat { int next, to, w; }e[N<<1];
void add(int u, int v, int w) {
	e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v; e[cnt].w=w;
	e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].to=u; e[cnt].w=w;
}

int dep[N], d[N], cdep, ans, mn;
int root, sz[N], vis[N];
void getroot(int x, int fa, int sum) {
	sz[x]=1; int y, mx=0;
	rdm(x, i) if(!vis[y=e[i].to] && e[i].to!=fa) {
		getroot(y, x, sum);
		sz[x]+=sz[y];
		mx=max(mx, sz[y]);
	}
	mx=max(mx, sum-mx);
	if(mx<mn) mn=mx, root=x;
}
void getdep(int x, int fa) {
	dep[++cdep]=d[x]; int y; //printf("x:%d\tfa:%d\tdep:%d\n", x, fa, dep[x]);
	rdm(x, i) if(!vis[y=e[i].to] && e[i].to!=fa) {
		d[y]=d[x]+e[i].w;
		getdep(y, x);
	}
}
int cal(int x, int last=0) {
	cdep=0; d[x]=last; //printf("root is:%d\n", x);
	getdep(x, -1); //puts("==========================");
	int ret=0, front=1, tail=cdep;
	sort(dep+1, dep+1+cdep);
	while(front<tail) {
		while(front<tail && dep[tail]+dep[front]>K) --tail;
		ret+=tail-front;
		++front;
	}
	return ret;
}
void dfs(int x, int all) {
	vis[x]=1; int y;
	ans+=cal(x); //printf("root:%d\n", x);
	rdm(x, i) if(!vis[y=e[i].to]) {
		ans-=cal(y, e[i].w);
		int s=sz[y]>sz[x]?all-sz[x]:sz[y];
		root=0; mn=oo; getroot(y, x, s);
		dfs(root, s);
	}
}

int main() {
	int n=getint();
	rep(i, n-1) { int u=getint(), v=getint(), w=getint(); add(u, v, w); }
	read(K); mn=oo;
	getroot((n+1)>>1, -1, n);
	dfs(root, n);
	printf("%d\n", ans);
	return 0;
}

  


Description

给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K

Input

N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k

Output

一行,有多少对点之间的距离小于等于k

Sample Input

7
1 6 13
6 3 9
3 5 7
4 1 3
2 4 20
4 7 2
10

Sample Output

5

HINT

Source

LTC男人八题系列

时间: 2024-10-12 07:27:25

【BZOJ】1468: Tree(点分治)的相关文章

bzoj 1468 Tree(点分治模板)

1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1527  Solved: 818[Submit][Status][Discuss] Description 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K Input N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k Output 一行,有多少对点之间的距离小于等于k Sample Input 7 1 6 13 6

BZOJ.1468.Tree(点分治)

题目链接 BZOJ1468 POJ1741 题意: 计算树上距离<=K的点对数 我们知道树上一条路径要么经过根节点,要么在同一棵子树中. 于是对一个点x我们可以这样统计: 计算出所有点到它的距离dep[],排序后可以O(n)求得<=K的点对数量. 但画个图后我们可以发现,对于在同一棵子树中的路径被重复计算过了.于是我们Ans-=Calc(v),减去一棵子树中的路径答案,但是这并不是之前x到它们的路径,于是给v的dep[]设一个初始值为w(x->v路径权值). 这样x的答案就计算完了,将这

[bzoj 1468] Tree

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1468 Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1517  Solved: 812[Submit][Status][Discuss] Description 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K Input N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k

BZOJ 1468 Tree 【模板】树上点分治

1 #include<cstdio> 2 #include<algorithm> 3 #define N 50010 4 #define M 500010 5 #define rg register 6 #define LL long long 7 using namespace std; 8 int n,m,cnt,root,tot,totnow,totbf; 9 int last[N],size[N],mxsize[N],dep[N],tmp[N],mg[N]; 10 LL a

点分治专题——bzoj 1468 &amp;bzoj 2152 题解

[前言]最近一直在忙着学算法,但是效果似乎不是很好.前段时间的树剖也快忘了= =.树套树没熟练,就开始写主席树了= =.更别说本身就不是很懂的莫比乌斯反演了.~~决定好好复习一下. [点分治的作用]套用SYC大神的话说是:用来解决树上路径点权统计问题. [大致流程] ①找出这颗树的重心. ②统计经过这个重心的答案 ③用重心把树割开 ④对每个"小树"做同样的事 [Q1--重心]其实找重心再进行计算只是为了不被卡链.什么是重心?就是当前树中的一个点K,使得MAX(SON[K])最小.SON

【BZOJ-1468】Tree 树分治

1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1025  Solved: 534[Submit][Status][Discuss] Description 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K Input N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k Output 一行,有多少对点之间的距离小于等于k Sample Input 7 1 6 13 6

[LeetCode] Convert Sorted List to Binary Search Tree(分治)

Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. 方法:为了使BST高度平衡,要找链表中的中值作为当前根节点. /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) :

【BZOJ 1468】Tree 点分治

点分治$O(nlogn)$ 坚持到月考结束后新校就剩下我一个OIer,其他人早已停课了,老师估计懒得为我一个人开机房门,让我跟班主任说了一声,今晚就回到了老校,开始了自己都没有想到会来的这么早的停课生活. 所以先水一道点分治 #include<cstdio> #include<algorithm> #define read(x) x=getint() #define N 40003 #define max(a,b) (a)>(b)?(a):(b) using namespac

1468. Tree【点分治】

Description 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K Input N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k Output 一行,有多少对点之间的距离小于等于k Sample Input 7 1 6 13 6 3 9 3 5 7 4 1 3 2 4 20 4 7 2 10 Sample Output 5 点分治模板 1 #include<iostream> 2 #include<cstring&