POJ1741 Tree(树分治)

题意:

求树上距离小于等于K的点对有多少个

思路:

每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心

找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K

但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。

/* ***********************************************
Author        :devil
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <string>
#include <cmath>
#include <stdlib.h>
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ou(a) printf("%d\n",a)
#define pb push_back
#define mkp make_pair
template<class T>inline void rd(T &x)
{
    char c=getchar();
    x=0;
    while(!isdigit(c))c=getchar();
    while(isdigit(c))
    {
        x=x*10+c-‘0‘;
        c=getchar();
    }
}
#define IN freopen("in.txt","r",stdin);
#define OUT freopen("out.txt","w",stdout);
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int N=1e4+10;
int n,k,x,y,w,root,ans,cnt,m;
int sum[N],ma[N],deep[N],d[N];
bool vis[N];
vector<pair<int,int> >eg[N];
void dfs(int u,int fa)
{
    sum[u]=1,ma[u]=0;
    for(int i=0;i<eg[u].size();i++)
    {
        int v=eg[u][i].first;
        if(v==fa||vis[v]) continue;
        dfs(v,u);
        sum[u]+=sum[v];
        ma[u]=max(ma[u],sum[v]);
    }
    ma[u]=max(ma[u],m-sum[u]);
    if(ma[u]<ma[root]) root=u;
}
void getdeep(int u,int fa)
{
    deep[++deep[0]]=d[u];
    for(int i=0;i<eg[u].size();i++)
    {
        int v=eg[u][i].first;
        if(v==fa||vis[v]) continue;
        d[v]=d[u]+eg[u][i].second;
        getdeep(v,u);
    }
}
int cal(int u,int now)
{
    d[u]=now;deep[0]=0;
    getdeep(u,0);
    sort(deep+1,deep+1+deep[0]);
    int ret=0,l=1,r=deep[0];
    while(l<r)
    {
        if(deep[l]+deep[r]<=k)
        {
            ret=ret+r-l;
            l++;
        }
        else r--;
    }
    return ret;
}
void work(int u)
{
    ans+=cal(u,0);
    vis[u]=1;
    for(int i=0;i<eg[u].size();i++)
    {
        int v=eg[u][i].first;
        if(vis[v]) continue;
        ans-=cal(v,eg[u][i].second);
        m=sum[v];
        root=0;
        dfs(v,0);
        work(root);
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    //IN
#endif
    ma[0]=inf;
    while(~scanf("%d%d",&n,&k)&&n)
    {
        for(int i=1; i<=n; i++) eg[i].clear();
        root=0;ans=0;cnt=0;m=n;
        memset(vis,0,sizeof(vis));
        for(int i=1; i<n; i++)
        {
            scanf("%d%d%d",&x,&y,&w);
            eg[x].pb(mkp(y,w));
            eg[y].pb(mkp(x,w));
        }
        dfs(1,0);
        work(root);
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-10-27 20:50:18

POJ1741 Tree(树分治)的相关文章

【POJ1741】Tree 树分治 模板咯?

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44302921"); } 题意: 给你一棵无根树,求有多少点对之间距离<=K. 题解: 树分治. 然后对于一个重心X,我们把它的所有子树中的所有点存到结构体数组中. 结构体中存距离和子树编号. 第一遍sort,我们双指针扫哪些点

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——点分治

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

HDU 4812 D Tree 树分治+逆元+hash新姿势

题意: 给定n个点的树 K 下面n个数是点权 下面n-1行给出树边. 问: 是否存在一条路径使得路径上点权积 % mod  = K 若存在则输出路径的两端. 若存在多条路径则输出字典序最小的一条. 思路: 按树重心分治. 分成路径是否经过树重心. 然后用力码.. has[x] = u; 表示乘积为x 对应的点是u 但这样has就不能用计数器来优化清空. 所以用2个数组: has[x] = cnt; has_id[x] = u; 这样has里存的是乘积为x是否存在.has_id[x] 来记录点.

[bzoj1468][poj1741]Tree[点分治]

可以说是点分治第一题,之前那道的点分治只是模模糊糊,做完这道题感觉清楚了很多,点分治可以理解为每次树的重心(这样会把数分为若干棵子树,子树大小为log级别),然后统计包含重心的整个子树的值减去各个子树的值,这样算出的就是与这个重心有关的情况的答案,比如这道题,求路径,那么就考虑在重心所在的子树中所有的路径减去不过重心的路径就是过重心的路径了.之前重心没找对...poj时间卡的紧就T了.. 1 #include <iostream> 2 #include <algorithm> 3