男人8题之 Tree (pku 1741)(树分治

题目:求一棵树上路径长度小于k的路径条数。

思路:这是LTC的男人八题里比较简单的一道。首先如果不是树,而是链的话,我们 可以想到一种分治算法(当然链的情况不分治更快),就是对于一个中点,对答案有贡献的要么是跨越中点的路径,要么是两边的路径,那么每次从中点分开,进行 分治的话复杂度是O(nlogn),对于这个树上的情况思路也是一样的,但是树上的分治有个比较特殊的地方是这个中点不太好找,需要跑一次dfs。然后对 每个分开的子树递归计算。我的实现总共用了5个递归,似乎可以少用一个(算子树的节点数目的时候),但是没想清楚怎么去,就索性直接又dfs了一遍。

另外,除了这种点分治,还可以使用边分治,但是边分治有一种难以避免的使复杂度大大增加的情况(处理方法似乎比较复杂),所以树分治的首选还是点分治。

/*
* @author:  Cwind
* http://www.cnblogs.com/Cw-trip/
* 蒟蒻只能做几个水题。。
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF 1000000000
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;

const int maxn=2e4+300;
struct EDGE{
    int to,d;
};
vector<int> G[maxn];
EDGE es[maxn];
int eh;
int center,mins;
bool vis[maxn];
vector<int> dis;
int n,k;
int cnt;
int getCenter(int v,int f){
    int ssum=0,maxs=0;
    for(int i=0;i<G[v].size();i++){
        int e=G[v][i];
        int u=es[e].to;
        if(vis[u]||u==f) continue;
        int aa=getCenter(u,v);
        if(aa>maxs) maxs=aa;
        ssum+=aa;
    }
    if(max(maxs,cnt-ssum-1)<mins){
        center=v;
        mins=max(maxs,cnt-ssum-1);
    }
    return ssum+1;
}
void dfs(int v,int fore,int f){
    dis.pb(fore);
    for(int i=0;i<G[v].size();i++){
        int e=G[v][i];
        int u=es[e].to;
        if(vis[u]||u==f) continue;
        dfs(u,fore+es[e].d,v);
    }
}
void cont(int v,int f){
    cnt++;
    for(int i=0;i<G[v].size();i++){
        int e=G[v][i];
        int u=es[e].to;
        if(vis[u]||u==f) continue;
        cont(u,v);
    }
}
int cul(int v,int fore){
    int ans=0;
    dis.clear();
    dfs(v,fore,-1);
    sort(dis.begin(),dis.end());
    int p=0,q=dis.size()-1;
    while(p<=q){
        while(p<=q&&dis[q]+dis[p]>k) q--;
        if(p>q) break;
        ans+=q-p;
        p++;
    }
    return ans;
}
int solve(int v){
    vis[v]=1;
    int ans=0;
    ans+=cul(v,0);
    for(int i=0;i<G[v].size();i++){
        int e=G[v][i];
        int u=es[e].to;
        if(vis[u]) continue;
        ans-=cul(u,es[e].d);
        mins=1e8;
        cnt=0;
        cont(u,-1);
        getCenter(u,-1);
        ans+=solve(center);
    }
    return ans;
}
void addedge(int from,int to,int d){
    es[eh].to=to,es[eh].d=d;
    G[from].pb(eh++);
    es[eh].to=from,es[eh].d=d;
    G[to].pb(eh++);
}
void init(){
    eh=0;
    for(int i=0;i<=n;i++)
        G[i].clear();
    memset(vis,0,sizeof vis);
    dis.clear();
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("defense.in","r",stdin);
    //freopen("defense.out","w",stdout);
    while(cin>>n>>k){
        if(n==0&&k==0) break;
        init();
        for(int i=0;i<n-1;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            addedge(a,b,c);
        }
        mins=1e9;
        dfs(1,0,-1);
        cnt=0;
        cont(1,-1);
        getCenter(1,-1);
        cout<<solve(center)<<endl;
    }
    return 0;
}

时间: 2024-10-02 19:19:31

男人8题之 Tree (pku 1741)(树分治的相关文章

POJ 1741 树分治

题目链接[http://poj.org/problem?id=1741] 题意: 给出一颗树,然后寻找点对(u,v)&&dis[u][v] < k的对数. 题解: 这是一个很经典的树分治的题.假设我们选择了一个参考点u,那么对于不同的点对(u,v),(u , v)之间的路径有两种情况,经过点u,和不经过点u,加入我算出了没有经过点u的对数,然后把经过点u的加起来就是答案了,很简单,这就是分治的思想.具体看代码. #include<cstdio> #include<c

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

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

POJ 1741 Tree (树上点分治)(楼教主男人八题之一)

题目地址:POJ 1741 树分治第一发! 树分治详情请看漆子超的国家集训队论文,论文传送门 树分治裸题. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #

男人八题_POJ-1741

题目链接:http://poj.org/problem?id=1741 树分治相关论文:http://wenku.baidu.com/view/60c6aa1ffc4ffe473368aba8.html 由于论文中有该题的讲解,所以便不再赘述.代码如下 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std;

POJ 1741 Tree(树分治|ltc男人八题)

 题意:求树上距离小于等于K的点对有多少个. 思路:这道题很容易想到树分治,对于当前的根节点来说,任意两个结点之间要么过根结点,要么在一棵子树中. 那么我们dfs一次求出所有点到根结点的距离,然后用o(n)的时间判定有多少节点对符合,(判断方法稍后说) 但是这样有很多在一颗子树中的节点对我们会求重复,我们需要减去在一棵子树中结点对小于等于k的数量,也就是说,我们这一步求的是在不同子树中距离小于等于k的节点对的个数. 接下来说判定方法,将每个点到根结点的距离排序,用两个指针指向队首和队尾,当结

poj 1741 楼教主男人八题之一:树分治

http://poj.org/problem?id=1741 Description 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

POJ 1741 男人八题——树分治

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 23829   Accepted: 7900 Description 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

POJ 1741 Tree ——(树分治)

思路参考于:http://blog.csdn.net/yang_7_46/article/details/9966455,不再赘述. 复杂度:找树的重心然后分治复杂度为logn,每次对距离数组dep排序复杂度为nlogn,而找重心的复杂度为dfs的复杂度--O(n),因此总的复杂度为O(nlognlogn). 因为第一次写树分治,留个代码: 1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h>

LCT男人八题系列

楼教的男人八题名气甚大,今天做了一道感觉还是涨了不少姿势的,然而估计之后的每道题都要看题解吧,姑且先记录一下.以后再做再更 1737 Connected Graph 1100 [email protected] 1738 An old Stone Game 407 [email protected] 1739 Tony's Tour 671 [email protected] 1740 A New Stone Game 2240 [email protected] 1741 Tree 1728