首页 > 代码库 > 【BZOJ】1468: Tree(点分治)
【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
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男人八题系列
【BZOJ】1468: Tree(点分治)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。