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

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 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

【样例输出】

8

【来源】

POJ 1741 Tree
男人八题 Problem E

题解:

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

这是点分治裸题。。漆子超论文上有讲过。

我们其实就是把整棵树的问题转化为在各个子树和子树之间的问题,于是我们就可以分治。

具体做法是先找重心,然后计算子树中各个点到当前子树的根的距离,然后考虑跨越子树的情况,最后加到一起就好了。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 10010
using 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;
}
时间: 2024-10-20 10:47:17

【点分治】【POJ 1741】【cogs 1714】树上的点对的相关文章

点分治——POJ 1741

写的第一道点分治的题目,权当认识点分治了. 点分治,就是对每条过某个点的路径进行考虑,若路径不经过此点,则可以对其子树进行考虑. 具体可以看menci的blog:点分治 来看一道例题:POJ 1741 Tree 题目大意:扔给你一颗有权无根树,求有多少条路径的长度小于k: 解题思路:先找出重心,用一次dfs处理出每个点到根的距离dis,然后将dis[]排序,用O(n)的复杂度处理出"过根且长度小于等于k的路径数目",删除根节点,对于每棵子树重复上述操作. 注意要去重: 像上面这样一个图

树上点分治 poj 1741

Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k. Wri

POJ 1741 Tree (树上点分治)(楼教主男人八题之一)

题目地址:POJ 1741 树分治第一发! 树分治详情请看漆子超的国家集训队论文,论文传送门 树分治裸题. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #

【POJ 1741】 Tree (树的点分治)

Tree Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v)

POJ - 1987 Distance Statistics 树上的分治

题目大意:和poj 1741的那题和类似,求树上节点之间的距离小于等于k的节点对有多少对 解题思路:具体可参考:<分治算法在树的路径问题中的应用--漆子超> 给这题的输入坑了,注意输入,不然会超时 #include<cstdio> #include<vector> #include<algorithm> using namespace std; #define maxn 40010 int vis[maxn], Sum[maxn], d[maxn], dp[

POJ 1987 Distance Statistics (树上点分治)

题目地址:POJ 1987 点分治模板题,跟POJ 1741几乎一样,.. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #include <stdi

POJ 1741 树分治(点分治模板题)

POJ 1741 题意:求一棵树中点对<=k的数量. 总结:点分治,搞不太懂..大概敲了一遍 #include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stac

poj 1741 树的点分治(入门)

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 18205   Accepted: 5951 Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an

树的点分治 (poj 1741, 1655(树形dp))

poj 1655:http://poj.org/problem?id=1655 题意: 给无根树,  找出以一节点为根,  使节点最多的树,节点最少. 题解:一道树形dp,先dfs 标记 所有节点的子树的节点数. 再dfs  找出以某节点为根的最大子树,节点最少. 复杂度(n) /***Good Luck***/ #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cs