zoukankan      html  css  js  c++  java
  • 【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(nlog_{a}n)$,具体证明看算导。。(很简单的。。。。。

    然而在树分治中,最多递归log次(因为是找了重心),因此复杂度降到$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	fa:%d	dep:%d
    ", 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
    ", 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
    ", 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
    ", 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

  • 相关阅读:
    struts2 constant详解
    大图片上传(ImageIO,注意有的图片不能上传时因为他是tiff格式)
    Spring提供的iBatis的SqlMap配置
    2013-7-31hibernate二级缓存
    2013-7-30。。。。难得闲
    POI导出大量数据的简单解决方案
    Tomcat优化详细2
    Tomcat优化详细1
    Tomcat优化方案
    java链表实现
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4172279.html
Copyright © 2011-2022 走看看