POJ

题意:给你一颗树,然后问你有多少小于k的路径

思路:点分治入门题,网上有很多关于点分治的博客,我也看了很久的代码,想了很久。

大概点分治就是一种通过不断找树的重心来降低复杂度的一种算法,因为数据范围很小,看其他聚聚的博客说一般点分治的题,数据范围都暗示1e4;

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn=10005;
struct edge
{
    int to,next,w;
}a[maxn<<1];
int n,m,k,head[maxn],cnt;
int root,sum,vis[maxn],sz[maxn];
int f[maxn],dep[maxn],stk[maxn],top;
int ans;
void addedge(int u,int v,int w)
{
    a[++cnt].to=v;
    a[cnt].next=head[u];
    a[cnt].w=w;
    head[u]=cnt;
}
void getroot(int u,int fa)
{
    sz[u]=1;f[u]=0;
    for(int e=head[u];e;e=a[e].next){
        int v=a[e].to;
        if(v==fa||vis[v])continue;
        getroot(v,u);
        sz[u]+=sz[v];
        f[u]=max(f[u],sz[v]);
    }
    f[u]=max(f[u],sum-sz[u]);
    if(f[u]<f[root])root=u;
}

void getdep(int u,int fa)
{
    stk[++top]=dep[u];
    for(int e=head[u];e;e=a[e].next){
        int v=a[e].to;
        if(v==fa||vis[v])continue;
        dep[v]=dep[u]+a[e].w;
        getdep(v,u);
    }
}
int calc(int u,int d0)
{
    top=0;dep[u]=d0;
    getdep(u,0);
    sort(stk+1,stk+top+1);
    int l=1,r=top,res=0;
    while(l<r){
        if(stk[l]+stk[r]<=k)res+=r-l,l++;
        else r--;
    }
    return res;
}


void solve(int u)
{
    ans+=calc(u,0);
    vis[u]=1;
    for(int e=head[u];e;e=a[e].next){
        int v=a[e].to;
        if(vis[v])continue;
        ans-=calc(v,a[e].w);
        sum=sz[v];root=0;
        getroot(v,0);
        solve(root);
    }
}

int main()
{
    while(~scanf("%d%d",&n,&k)){
        if(n==0&&k==0)break;
        memset(head,0,sizeof(head));
        memset(vis,0,sizeof(vis));
        cnt=0;ans=0;
        for(int i=1;i<n;++i){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        root=0;sum=f[0]=n;
        getroot(1,0);
        solve(root);
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/lalalatianlalu/p/9769743.html