<span>POJ1741 Tree(树分治)</span>

 题意:

求树上距离小于等于K的点对有多少个

思路:

每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心

找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K

但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。

/* ***********************************************
Author        :devil
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <string>
#include <cmath>
#include <stdlib.h>
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ou(a) printf("%d\n",a)
#define pb push_back
#define mkp make_pair
template<class T>inline void rd(T &x)
{
    char c=getchar();
    x=0;
    while(!isdigit(c))c=getchar();
    while(isdigit(c))
    {
        x=x*10+c-'0';
        c=getchar();
    }
}
#define IN freopen("in.txt","r",stdin);
#define OUT freopen("out.txt","w",stdout);
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int N=1e4+10;
int n,k,x,y,w,root,ans,cnt,m;
int sum[N],ma[N],deep[N],d[N];
bool vis[N];
vector<pair<int,int> >eg[N];
void dfs(int u,int fa)
{
    sum[u]=1,ma[u]=0;
    for(int i=0;i<eg[u].size();i++)
    {
        int v=eg[u][i].first;
        if(v==fa||vis[v]) continue;
        dfs(v,u);
        sum[u]+=sum[v];
        ma[u]=max(ma[u],sum[v]);
    }
    ma[u]=max(ma[u],m-sum[u]);
    if(ma[u]<ma[root]) root=u;
}
void getdeep(int u,int fa)
{
    deep[++deep[0]]=d[u];
    for(int i=0;i<eg[u].size();i++)
    {
        int v=eg[u][i].first;
        if(v==fa||vis[v]) continue;
        d[v]=d[u]+eg[u][i].second;
        getdeep(v,u);
    }
}
int cal(int u,int now)
{
    d[u]=now;deep[0]=0;
    getdeep(u,0);
    sort(deep+1,deep+1+deep[0]);
    int ret=0,l=1,r=deep[0];
    while(l<r)
    {
        if(deep[l]+deep[r]<=k)
        {
            ret=ret+r-l;
            l++;
        }
        else r--;
    }
    return ret;
}
void work(int u)
{
    ans+=cal(u,0);
    vis[u]=1;
    for(int i=0;i<eg[u].size();i++)
    {
        int v=eg[u][i].first;
        if(vis[v]) continue;
        ans-=cal(v,eg[u][i].second);
        m=sum[v];
        root=0;
        dfs(v,0);
        work(root);
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    //IN
#endif
    ma[0]=inf;
    while(~scanf("%d%d",&n,&k)&&n)
    {
        for(int i=1; i<=n; i++) eg[i].clear();
        root=0;ans=0;cnt=0;m=n;
        memset(vis,0,sizeof(vis));
        for(int i=1; i<n; i++)
        {
            scanf("%d%d%d",&x,&y,&w);
            eg[x].pb(mkp(y,w));
            eg[y].pb(mkp(x,w));
        }
        dfs(1,0);
        work(root);
        printf("%d\n",ans);
    }
    return 0;
}

 

全部评论

相关推荐

勇敢的联想人前程似锦:如果我是你,身体素质好我会去参军,然后走士兵计划考研211只需要200多分。
点赞 评论 收藏
分享
HNU_fsq:建议直接出国,这简历太6了。自愧不如
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务