点分治 poj1741

题意:

给出一颗树,询问有多少对点对距离<=k

链接:

http://poj.org/problem?id=1741

题解:

点分治的模板题

点分治即采用分治思想分而治之

考虑一颗子树内距离<=k的两种情况

1.这两点连线不过根节点、

那么就是这个问题的一个子问题

2.这两点连线过根节点

那么从根节点开始dfs出deep数组

之后只需将deep数组排序,一个指针从head开始,一个指针从tail开始,只需满足dep[x]+dep[y]<=k即为满足的解

但会发现如果两个节点位于同一颗子树中是不能构成的,所以应dfs减去这些答案

**读入要优化

代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
using namespace std;
#define maxn 110000 
#define INF 98937894
int root,n,m,c,dd,e,l,ans,sum,b[maxn],head[maxn],son[maxn],f[maxn],vis[maxn],deep[maxn],d[maxn];
struct re{int a,b,c;}a[maxn*2];
void arr(int x,int y,int z)
{
    l++;
    a[l].a=head[x];
    a[l].b=y;
    a[l].c=z;
    head[x]=l;
}
void getroot(int x,int fa)
{
    son[x]=1; f[x]=0;
    int u=head[x];
    while (u!=0)
    {
        int v=a[u].b;
        if (!(v==fa||vis[v]))
        {
          getroot(v,x);
          son[x]+=son[v];
          f[x]=max(f[x],son[v]);
      }
        u=a[u].a;
    }
    f[x]=max(f[x],sum-son[x]);
    if (f[x]<f[root]) root=x;
}
void getdeep(int x,int fa)
{
    deep[++deep[0]]=d[x];
    int u=head[x];
    while (u!=0)
    {
        int v=a[u].b;
        if (!(v==fa||vis[v]))
        {
           d[v]=d[x]+a[u].c;
          getdeep(v,x);
      }
        u=a[u].a;
    }
}
int cal(int x,int v)
{
    d[x]=v; deep[0]=0;
    getdeep(x,0);
    sort(deep+1,deep+deep[0]+1);
    int l=1,r=deep[0],sum=0;
    while (l<r)
    {
        if (deep[l]+deep[r]<=m) sum+=r-l,l++;
        else r--;
    }
    return sum;
}
void solve(int x)
{
    ans+=cal(x,0);
    vis[x]=1;
    int u=head[x];
    while (u!=0)
    {
        int v=a[u].b;
        if (vis[v]!=1)
        { 
          ans-=cal(v,a[u].c);
          sum=son[v];
          root=0;
          getroot(v,x);
          solve(root);
      }
        u=a[u].a;
    }
}
int main()
{
    freopen("noip.in","r",stdin);
    freopen("noip.out","w",stdout);
    std::ios::sync_with_stdio(false); 
    cin>>n>>m;
    while (n!=0)
    {
    memset(vis,0,sizeof(vis));
        memset(head,0,sizeof(head));
        l=ans=root=0;    f[0]=INF;
        for (int i=1;i<=n-1;i++)
        {
          cin>>c>>dd>>e,arr(c,dd,e),arr(dd,c,e);
        }
        sum=n;
        getroot(1,0); 
        solve(root);
        cout<<ans<<endl;
        cin>>n>>m;
    }
    return 0;
}
View Code

 

posted @ 2018-01-02 23:54  尹吴潇  阅读(173)  评论(0编辑  收藏  举报