poj 1741 Tree 点分治

题目大意:
给定一棵N个节点、边上带权的树,再给出一个K,询问有多少个点对(u,v)满足dis[u,v]<=K。
数据规模:

多组测试数据,N≤10000,1≤边权≤1000,1≤K≤10^9。

马克:楼天城男人必做8题之一.... 目前只做了这一题哭

http://wenku.baidu.com/view/60c6aa1ffc4ffe473368aba8.html

上面网址是讲解思路的论文,具体实现没说。。


首先是要找到重心,那么先递归求出每个点拆掉后能够得到的最大的点的个数,这个点的个数最小的肯定是重心。

然后就按照论文讲解的方法求点对数量,先要求出每个点到重心的距离。

下面是代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
using namespace std;

const int MAXN = 10100;
const int inf = 0x7f7f7f7f;
typedef pair<int,int> PII;
int n,k;
int vis[MAXN],fa[MAXN];
int mson[MAXN],son[MAXN];
int nod[MAXN],ns;
int dis[MAXN],ds;
int ans;
vector<PII >vv[MAXN];
void dfs(int u,int p)
{
    son[u]=1;
    mson[u]=0;
    nod[ns++]=u;
    int siz=vv[u].size();
    for(int i=0;i<siz;i++)
    {
        int v=vv[u][i].first;
        if(vis[v]||v==p) continue;
        dfs(v,u);
        son[u]+=son[v];
        mson[u]=max(mson[u],son[v]);
    }
}
int getroot(int u)//求重心
{
    ns=0;
    dfs(u,-1);
    int mi=MAXN,res=-1;
    for(int i=0;i<ns;i++)
    {
        mson[nod[i]]=max(mson[nod[i]],son[u]-son[nod[i]]);//每个点拆掉后能够得到的最大的点的个数
        if(mson[nod[i]]<mi)
        {
            mi=mson[nod[i]];
            res=nod[i];
        }
    }
    return res;
}
void getdis(int u,int d,int p)//求出子节点到根的距离
{
    dis[ds++]=d;
    int siz=vv[u].size();
    for(int i=0;i<siz;i++)
    {
        int v=vv[u][i].first;
        if(vis[v]||v==p) continue;
        getdis(v,d+vv[u][i].second,u);
    }
}
void calc(int u,int t)  
{
   sort(dis,dis+ds);
   int i=dis[0]?0:1,j=ds-1;
   while(i<j)
   {
       if(dis[i]+dis[j]<=k)
       {
        ans+=(j-i)*t;
        i++;
       }
       else j--;
   }
}
void solve(int u)
{
    u=getroot(u);
    ds=0;
    getdis(u,0,-1);
    calc(u,1);         //求以u为根的子节点可以构成多少对
    vis[u]=1;
    for(int i=0;i<ds;i++)
        if(dis[i]<=k&&dis[i]!=0) ans++;//以根为起点的个数
    int siz=vv[u].size();
    for(int i=0;i<siz;i++)
    {
        int v=vv[u][i].first;
        if(vis[v]) continue;
        ds=0;
        getdis(v,vv[u][i].second,-1);   
        calc(v,-1);                   //减去属于同一个子树的重复点对
        solve(v);
    }

}
int main()
{
    while(~scanf("%d%d",&n,&k)&&(n||k))
    {
        for(int i=0;i<=n;i++) vv[i].clear();
        for(int i=1;i<n;i++)
        {
            int u,v,l;
            scanf("%d%d%d",&u,&v,&l);
            vv[u].push_back(make_pair(v,l));
            vv[v].push_back(make_pair(u,l));
        }
        ans=0;
        memset(vis,0,sizeof(vis));
        solve(1);
        printf("%d\n",ans);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值