poj 1741 楼教主男人八题之一:树分治

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

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) not exceed k.

Write a program that will count how many pairs which are valid for a given tree.

Input

The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.

The last test case is followed by two zeros.

Output

For each test case output the answer on a single line.

Sample Input

5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

Sample Output

8
/**
poj 1741  楼教主男人八题之一:树分治
题目大意:给定一个带权树,问有多少对点之间的距离之和不超过k
解题思路:典型的树分治算法。(转自ACMonster)
           最容易想到的算法是:从每个点出发遍历整棵树,统计数对个数。
           由于时间复杂度O(N^2),明显是无法满足要求的。

           对于一棵有根树, 树中满足要求的一个数对所对应的一条路径,必然是以下两种情况之一:
           1、经过根节点
           2、不经过根节点,也就是说在根节点的一棵子树中
           对于情况2,可以递归求解,下面主要来考虑情况1。

           设点i的深度为Depth[i],父亲为Parent[i]。
           若i为根,则Belong[i]=-1,若Parent[i]为根,则Belong[i]=i,否则Belong[i]=Belong[Parent[i]]。
           这三个量都可以通过一次BFS求得。
           我们的目标是要统计:有多少对(i,j)满足i<j,Depth[i]+Depth[j]<=K且Belong[i]<>Belong[j]

           如果这样考虑问题会变得比较麻烦,我们可以考虑换一种角度:
           设X为满足i<j且Depth[i]+Depth[j]<=K的数对(i,j)的个数
           设Y为满足i<j,Depth[i]+Depth[j]<=K且Belong[i]=Belong[j]数对(i,j)的个数
           那么我们要统计的量便等于X-Y

           求X、Y的过程均可以转化为以下问题:
           已知A[1],A[2],...A[m],求满足i<j且A[i]+A[j]<=K的数对(i,j)的个数

           对于这个问题,我们先将A从小到大排序。
           设B[i]表示满足A[i]+A[p]<=K的最大的p(若不存在则为0)。我们的任务便转化为求出A所对应的B数组。那么,若B[i]>i,那么i对答案的贡献为B[i]-i。
           显然,随着i的增大,B[i]的值是不会增大的。利用这个性质,我们可以在线性的时间内求出B数组,从而得到答案。

           综上,设递归最大层数为L,因为每一层的时间复杂度均为“瓶颈”——排序的时间复杂度O(NlogN),所以总的时间复杂度为O(L*NlogN)

           然而,如果遇到极端情况——这棵树是一根链,那么随意分割势必会导致层数达到O(N)级别,对于N=10000的数据是无法承受的。因此,我们在每一棵子树中选择“最优”的点分割。所谓“最优”,是指删除这个点后最大的子树尽量小。这个点可以通过树形DP在O(N)时间内求出,不会增加时间复杂度。这样一来,即使是遇到一根链的情况时,L的值也仅仅是O(logN)的。

           因此,改进后算法时间复杂度为O(Nlog^2N),可以AC。
*/
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
const int maxn=10015;
const int INF=0x3f3f3f3f;
int head[maxn],ip;
int siz[maxn],can[maxn],lst[maxn],d[maxn],fa[maxn];
int ans,tl,n,k,l1,l2;

void init()
{
    memset(head,-1,sizeof(head));
    memset(can,0,sizeof(can));
    ip=0;
    ans=0;
}

struct note
{
    int v,w,next;
} edge[maxn*2];

void addedge(int u,int v,int w)
{
    edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
}

void dfs1(int u,int pre)
{
    siz[u]=1;
    lst[++tl]=u;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==pre||can[v])continue;
        dfs1(v,u);
        fa[v]=u;
        siz[u]+=siz[v];
    }
}
int getroot(int u,int pre)
{
    tl=0;
    dfs1(u,pre);
    int pos,tmp=INF,d,y;
    for(int i=1; i<=tl; i++)
    {
        d=0,y=lst[i];
        for(int p=head[y]; p!=-1; p=edge[p].next)
        {
            int v=edge[p].v;
            if(v==fa[y]||can[v])continue;
            d=max(d,siz[v]);
        }
        if(y!=u)
            d=max(d,siz[u]-siz[y]);
        if(d<tmp)pos=y,tmp=d;
    }
    return pos;
}

void dfs2(int u,int pre,int dis)
{
    lst[++l1]=u;
    d[u]=dis;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==pre||can[v])continue;
        dfs2(v,u,dis+edge[i].w);
    }
}

int getans(int *a,int l,int r)
{
    int j=r,ret=0;
    for(int i=l; i<=r; i++)
    {
        while(d[a[i]]+d[a[j]]>k&&j>i)j--;
        ret+=j-i;
        if(j==i)break;
    }
    return ret;
}

inline bool cmp(int i,int j)
{
    return d[i]<d[j];
}
void work(int u,int pre)
{
    int root=getroot(u,pre);
    l1=l2=0;
    for(int i=head[root]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].v;
        if(can[v]==0)
        {
            l2=l1;
            dfs2(v,root,edge[i].w);
            sort(lst+l2+1,lst+l1+1,cmp);
            ans-=getans(lst,l2+1,l1);
        }
    }
    lst[++l1]=root,d[root]=0;
    sort(lst+1,lst+l1+1,cmp);
    ans+=getans(lst,1,l1);
    can[root]=1;
    for(int i=head[root]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].v;
        if(can[v]==0)
            work(v,root);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        if(n==0&&k==0)break;
        init();
        for(int i=0; i<n-1; i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            addedge(x,y,z);
            addedge(y,x,z);
        }
        work(1,0);
        printf("%d\n",ans);
    }
    return 0;
}

时间: 2024-11-05 18:42:25

poj 1741 楼教主男人八题之一:树分治的相关文章

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 1743 Musical Theme(男人八题&amp;后缀数组第一题)

Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 17298   Accepted: 5939 Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the

LCT男人八题系列

楼教的男人八题名气甚大,今天做了一道感觉还是涨了不少姿势的,然而估计之后的每道题都要看题解吧,姑且先记录一下.以后再做再更 1737 Connected Graph 1100 [email protected] 1738 An old Stone Game 407 [email protected] 1739 Tony's Tour 671 [email protected] 1740 A New Stone Game 2240 [email protected] 1741 Tree 1728

POJ1742 Coins(男人八题之一)

前言 大名鼎鼎的男人八题,终于见识了... 题面 http://poj.org/problem?id=1742 分析 § 1 多重背包 这很显然是一个完全背包问题,考虑转移方程: DP[i][j]表示用前i种硬币能否取到金额j,ture表示可以,false表示不行. 则有 DP[i][j] = DP[i - 1][j] | DP[i - 1][j - k * Ai], 0 ≤ k ≤ Ci, j - k * Ai ≥ 0 这是一个O(N3)的算法,考虑到数据范围1 ≤ N ≤ 100, M ≤

男人八题2019

打的第三年男人八题了= = 感觉自己可能能创造一个EZ历史上打过最多男人八题的人(嘤嘤嘤我明明是妹子啊 考场上是5题 目前补了7题 目录 Biology Chemistry Chinese English Geography History Math Physics Biology 模拟= = //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cm

POJ 1741 男人八题——树分治

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 23829   Accepted: 7900 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

题目链接:http://poj.org/problem?id=1741 树分治相关论文:http://wenku.baidu.com/view/60c6aa1ffc4ffe473368aba8.html 由于论文中有该题的讲解,所以便不再赘述.代码如下 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std;

hdu 2844 Coins 多重背包模板题 ,二进制优化。据说是楼教主的男人八题之一

Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8052    Accepted Submission(s): 3291 Problem Description Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One

POJ 1741 Tree(树分治|ltc男人八题)

 题意:求树上距离小于等于K的点对有多少个. 思路:这道题很容易想到树分治,对于当前的根节点来说,任意两个结点之间要么过根结点,要么在一棵子树中. 那么我们dfs一次求出所有点到根结点的距离,然后用o(n)的时间判定有多少节点对符合,(判断方法稍后说) 但是这样有很多在一颗子树中的节点对我们会求重复,我们需要减去在一棵子树中结点对小于等于k的数量,也就是说,我们这一步求的是在不同子树中距离小于等于k的节点对的个数. 接下来说判定方法,将每个点到根结点的距离排序,用两个指针指向队首和队尾,当结