【POJ1741】Tree 树分治 模板咯?

广告:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44302921");
}

题意:

给你一棵无根树,求有多少点对之间距离<=K。

题解:

树分治。

然后对于一个重心X,我们把它的所有子树中的所有点存到结构体数组中。

结构体中存距离和子树编号。

第一遍sort,我们双指针扫哪些点对符合要求。

第二遍sort,我们把同一子树中还符合要求的点对数目删掉。

sort是O(nlogn),处理是O(n)。

然后分治logn层,总时间复杂度O(nlog2n)

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 50100
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
struct Eli
{
    int v,len,next;
}e[N<<1];
int head[N],cnt;
bool rem[N];
inline void add(int u,int v,int len)
{
    e[++cnt].v=v;
    e[cnt].len=len;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int f[N],f2[N],son[N];
int TreeCenter,length;
int dfs1(int x,int p)
{
    int i,v;
    f[x]=f2[x]=0;
    for(i=head[x];i;i=e[i].next)
    {
        v=e[i].v;
        if(v==p||rem[v])continue;
        int temp=dfs1(v,x);
        if(temp>f[x])
        {
            f2[x]=f[x];
            f[x]=temp;
            son[x]=v;
        }
        else if(temp>f2[x])
            f2[x]=temp;
    }
    return f[x]+1;
}
int g[N];
void dfs2(int x,int p)
{
    int i,v;
    if(max(f[x],g[x])<length)
    {
        length=max(f[x],g[x]);
        TreeCenter=x;
    }
    for(i=head[x];i;i=e[i].next)
    {
        v=e[i].v;
        if(v==p||rem[v])continue;
        if(v==son[x])g[v]=max(g[x],f2[x])+1;
        else g[v]=max(g[x],f[x])+1;
        dfs2(v,x);
    }
    return ;
}
inline int get_TreeCenter(int x)
{
    if(rem[x])return 0;
    length=inf,g[x]=0;
    dfs1(x,0),dfs2(x,0);
    return TreeCenter;
}
struct Summorer
{
    int a,b;
    // 权值,子树标号
    Summorer(int _a=0,int _b=0):a(_a),b(_b){}
}src[N];
inline int cmpa(Summorer a,Summorer b)
{
    return a.a<b.a;
}
inline int cmpb(Summorer a,Summorer b)
{
    return a.b==b.b?a.a<b.a:a.b<b.b;
}
int size[N];
void dfs3(int x,int p,int t,int len)
{
    src[++cnt]=Summorer(len,t);
    size[x]=1;
    int i,v;
    for(i=head[x];i;i=e[i].next)
    {
        v=e[i].v;
        if(rem[v]||v==p)continue;
        dfs3(v,x,t,len+e[i].len);
        size[x]+=size[v];
    }
    return ;
}
int ans;
void work(int x,int n)
{
    if(!n)return ;
    rem[x=get_TreeCenter(x)]=1;
    int num=0,i,j,k,v,p;
    cnt=0;
    for(i=head[x];i;i=e[i].next)
    {
        v=e[i].v;
        if(rem[v])continue;
        dfs3(v,x,++num,e[i].len);
    }
    sort(src+1,src+cnt+1,cmpa);
    p=n;
    for(i=0;i<=n;i++)
    {
        while(i<p&&src[i].a+src[p].a>m)p--;
        if(i>=p)break;
        ans+=(p-i);
    }
    sort(src+1,src+cnt+1,cmpb);
    src[cnt+1].b=-1;
    for(i=0;i<=n;i=j)
    {
        for(j=i+1;src[j].b==src[i].b;j++);
        for(p=j-1,k=i;k<j;k++)
        {
            while(k<p&&src[k].a+src[p].a>m)p--;
            if(k>=p)break;
            ans-=(p-k);
        }
    }
    for(i=head[x];i;i=e[i].next)
    {
        v=e[i].v;
        if(rem[v])continue;
        work(v,size[v]-1);
    }
    return ;
}
void init()
{
    ans=cnt=0;
    memset(rem,0,sizeof rem);
    memset(head,0,sizeof head);
}
int main()
{
    int i,a,b,c;

    while(scanf("%d%d",&n,&m),(n|m))
    {
        init();
        for(i=1;i<n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c),add(b,a,c);
        }
        work(1,n-1);
        printf("%d\n",ans);
    }

    return 0;
}
时间: 2024-08-11 02:24:58

【POJ1741】Tree 树分治 模板咯?的相关文章

poj 1744 tree 树分治

Tree Time Limit: 1000MS   Memory Limit: 30000K       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 ve

【BZOJ-1468】Tree 树分治

1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1025  Solved: 534[Submit][Status][Discuss] Description 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K Input N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k Output 一行,有多少对点之间的距离小于等于k Sample Input 7 1 6 13 6

HDU 4812 D Tree 树分治+逆元处理

D Tree Problem Description There is a skyscraping tree standing on the playground of Nanjing University of Science and Technology. On each branch of the tree is an integer (The tree can be treated as a connected graph with N vertices, while each bran

poj1741 Tree 点分治

入门题,算是对树分治有了初步的理解吧. #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<vector> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using nam

POJ1741 经典树分治

题意:有一棵树,每条边有一个距离,求dis(u,v)<=k的点的对数 题解:树分治,对于一颗树上的两点,要么在同一颗子树上,要么在不同子树上,要么一个点是根,另一个在某一子树上,对于第一种情况我们可以通过递归来变成第二种或者第三种情况.我们对于某一颗子树来说我们先统计dis[u]+dis[v]<=k的点的对数,然后把该子树的所有子节点为根的这颗子树中dis[u]+dis[v]<=k的点的对数删去,因为在递归到后面会计算重复(这是第一种情况),所以这样就得到了该子树的满足条件的点对数,那么

POJ 1741 Tree 树分治(点分治)

题意:给你一颗带边权树,问你其中 dis(v,u) <= k 的对数 解题思路: 首先推荐大家看 09年国家集训队漆子超 的论文 看到这题  我们可以有三种思路 第一种是枚举起点,遍历整颗树找对数    时间复杂度 为  O(n^2),空间复杂度为 O(n) 第二种是树形dp的思想     每个节点用 长度为 K 数组维护 ,递归求解  时间复杂度为 O(n ^k)空间复杂度 为 O(n) 第三种就是我们要用到的点分治的思想. 这种思想的具体思路是  先找到一个  根  对这个根进行 深搜, 找

[poj1741]Tree(点分治+容斥原理)

题意:求树中点对距离<=k的无序点对个数. 解题关键:树上点分治,这个分治并没有传统分治的合并过程,只是分成各个小问题,并将各个小问题的答案相加即可,也就是每层的复杂度并不在合并的过程,是在每层的处理过程. 此题维护的是树上路径,考虑点分治. 点分治的模板题,首先设点x到当前子树跟root的距离为,则满足${d_x} + {d_y} \le k$可以加进答案,但是注意如果x,y在同一棵子树中,就要删去对答案的贡献,因为x,y会在其所在的子树中在计算一次.同一棵子树中不必考虑是否在其同一棵子树中的

POJ - 1741 - Tree - 点分治 模板

POJ-1741 题意: 对于带权的一棵树,求树中距离不超过k的点的对数. 思路: 点分治的裸题. 将这棵树分成很多小的树,分治求解. #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include

POJ1741:Tree——点分治

题面 POJ1741 解析  刚学了点分治,练一练模版题 过程就不多说了,主要说说细节 在每次查询下一棵子树时, 传进去的整棵子树大小是上一次的$siz$, 这个数据其实是错的, 但好像并不影响时间复杂度, 这样的话找重心就必须找最大子树最小的点了,否则会错.因此需要存一个当前最大子树最小的点的最大子树的大小, 以及当前的重心, 每次找重心之前,前者要赋为$inf$ 在每次统计以当前点为$lca$的链的答案时, 要先把这个重心加入数组中, 再按$dis$排序, 然后用双指针法或是其他可行的方法统