【点分治】【POJ 1741】【cogs 1714】树上的点对

来源:互联网 发布:淘宝开通信用卡 编辑:程序博客网 时间:2024/04/29 17:38

1714. [POJ1741][男人八题]树上的点对

★★★   输入文件:poj1741_tree.in   输出文件:poj1741_tree.out   简单对比时间限制:1 s   内存限制:256 MB

【题目描述】

给一棵有n个节点的树,每条边都有一个长度(小于1001的正整数)。
定义dist(u,v)=节点u到节点v的最短路距离。
给出一个整数k,我们称顶点对(u,v)是合法的当且仅当dist(u,v)不大于k。
写一个程序,对于给定的树,计算有多少对顶点对是合法的。

【输入格式】

输入包含多组数据。
每组数据的第一行有两个整数N,K(N<=10000)。接下来N-1行每行有三个整数u,v,l,代表节点u和v之间有一条长度l的无向边。
输入结束标志为N=K=0.

【输出格式】

对每组数据输出一行一个正整数,即合法顶点对的数量。

【样例输入】

5 41 2 31 3 11 4 23 5 10 0

【样例输出】

8

【来源】

POJ 1741 Tree男人八题 Problem E

题解:

题目背景:这是楼教主的“男人八题”的E题,当时还并没有点分这种算法,于是被楼教主开发了出来。。

这是点分治裸题。。漆子超论文上有讲过。
我们其实就是把整棵树的问题转化为在各个子树和子树之间的问题,于是我们就可以分治。
具体做法是先找重心,然后计算子树中各个点到当前子树的根的距离,然后考虑跨越子树的情况,最后加到一起就好了。

Code:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#define N 10010using namespace std;struct Edge{    int v,next,l;}edge[2*N];int n,k,root,size,num,ans;int head[N],son[N],h[N],dis[N],d[N];bool vis[N]={0};int in(){    int x=0; char ch=getchar();    while (ch<'0' || ch>'9') ch=getchar();    while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();    return x;}void add(int u,int v,int l){    edge[++num].v=v; edge[num].l=l;    edge[num].next=head[u]; head[u]=num;}void Focus(int x,int last){    son[x]=1; h[x]=0;    for (int i=head[x]; i; i=edge[i].next){        int v=edge[i].v;        if (vis[v] || v==last) continue;        Focus(v,x); son[x]+=son[v];        h[x]=max(h[x],son[v]);    }    h[x]=max(h[x],size-son[x]);    if (h[x]<h[root]) root=x;}void Dis(int x,int last){    d[++d[0]]=dis[x];    for (int i=head[x]; i; i=edge[i].next){        int v=edge[i].v;        if (vis[v] || v==last) continue;        dis[v]=dis[x]+edge[i].l;        Dis(v,x);    }}int Calc(int x,int p){    dis[x]=p,d[0]=0;    Dis(x,0); sort(d+1,d+d[0]+1);    int l=1,r=d[0],s=0;//  cout<<"now: "<<x<<endl;    while (l<r){        if (d[l]+d[r]<=k) s+=(r-l),l++;        else r--;//      cout<<"L: "<<l<<"  "<<"R: "<<r<<"  "<<"sum: "<<s<<endl;    }//  cout<<"now: "<<x<<"  "<<"sum: "<<s<<endl;    return s;}void Point(int x){    vis[x]=1; ans+=Calc(x,0);    for (int i=head[x]; i; i=edge[i].next){        int v=edge[i].v;        if (vis[v]) continue;        ans-=Calc(v,edge[i].l);        root=0; size=h[root]=son[v];        Focus(v,0); Point(root);    }//  cout<<"ans--->"<<ans<<endl;}void Work(){    root=0; size=h[root]=n;    memset(vis,0,sizeof(vis));    Focus(1,0); Point(root);}int main(){    while (n=in(),k=in()){        if (!n && !k) break;        num=ans=0;        memset(head,0,sizeof(head));        for (int i=1; i<n; i++){            int u=in(),v=in(),l=in();            add(u,v,l),add(v,u,l);        }        Work();        printf("%d\n",ans);    }    return 0;}
1 0
原创粉丝点击