poj 1741 Tree 点分治

来源:互联网 发布:淘宝互联软件是骗局吗 编辑:程序博客网 时间:2024/04/29 15:16
题目大意:
给定一棵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);    }}