cqyz oj | 树的分治 | 树形DP | 树的重心

Description

  给定一棵N个节点的带权树,定义dist(u,v)为u,v两点间的最短路径长度,路径的长度义为路径上所有边的权和。再给定一个K,如果对于不同的两个结点a,b,如果满足dist(a,b)<=K,则称(a,b)为合法点对。
  你的任务是求合法点对个数。

Input

  第一行包含两个个整数N和K,接下来的N-1行,每行包含三个整数:u,v,len,表示树边(u,v)的长度len。

Output

  一个整数,表示合法点对的数目。

Sample Input 1

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

Sample Output 1

8

Hint

0<N<100 000,0<K<1000000000,0<len<10000



看到数据范围先想到用long long
直接跳过O(n^2)暴力算法

满足条件的点a和b有以下两种情况
一、a,b之间的路径经过根,即a,b在根节点的两棵不同子树里,或者a,b其中之一是根节点
1.计算root到各点距离dis[j]
2.在dis[]中统计满足dis[a]+dis[b]<=K的点对数量X,转化为化装晚会一题
3.去掉来自同一子树中的点对数Y
答案为X-Y
二、a,b在之间的路径不经过root,即a,b在同一棵子树里
这种情况就是分治算法的子问题,可以递归求
(为什么要先X-Y再在子树里重新求呢?因为由“一、”里计算出来的点对本来可以不经过root,相当于多算了一段长度,导致有些原本满足条件的点对因为这截多算的长度而未被统计到)

由这个思路,设如下函数:
getdist(i)计算以i为根的子树中i到各点的距离 \(O(size[i])\)
F(i,k)计算以i为根的子树中dis[a]+dis[b]<=k的点对数量\(O(size[i]log_2size[i])\)
calc(i)递归函数 \(O(n)\)
该方法时间复杂度\(O(depth * nlogn)\),如果随便找一个根可能导致depth为n,时间退化为\(O(n^2logn)\)

要使depth最小,想到找树的重心,因此又设一个函数getroot(i)找i所在的子树里的重心为根,\(O(size[i])\)
这样时间稳定在\(O(n*(log_2n)^2)\)

此题代码实现对于我这种蒟蒻有一定难度,有很多函数调用之间的细节:

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100005
using namespace std;
typedef long long ll;
int fir[maxn],ne[maxn*2],to[maxn*2],w[maxn*2],newp=0;
void add(int x,int y,int z){
    ne[++newp]=fir[x];
    fir[x]=newp;
    to[newp]=y;
    w[newp]=z;
}
int siz[maxn],mn,rt;
bool mark[maxn]={0};
void getroot(int u,int f,int tot){
    siz[u]=1;
    int tmp=0;
    for(int i=fir[u];i;i=ne[i]){
        int v=to[i];
        if(v==f || mark[v])continue;
        getroot(v,u,tot);
        siz[u]+=siz[v];
        tmp=max(tmp,siz[v]);
    }
    tmp=max(tmp,tot-siz[u]);
    if(tmp<mn)rt=u,mn=tmp;
}

ll dis[maxn];
int cnt;
void getdist(int u,int f,ll dist){
    dis[cnt++]=dist;
    for(int i=fir[u];i;i=ne[i]){
        int v=to[i];
        if(v==f || mark[v])continue;
        getdist(v,u,dist+w[i]);
    }
}
ll F(int u,int k){
    cnt=0;
    getdist(u,0,0);
    sort(dis,dis+cnt);
    ll res=0;
    for(int i=0;i<cnt;i++)
        res+=upper_bound(dis,dis+i,k-dis[i])-dis;
    return res;
}
int n,K;
ll calc(int u){
    ll t=F(u,K);
    mark[u]=1;
    for(int i=fir[u];i;i=ne[i]){
        int v=to[i];
        if(mark[v])continue;
        t-=F(v,K-2*w[i]);
        mn=n;
        getroot(v,0,siz[v]);
        t+=calc(rt);
    }
    return t;
}
int main(){
    scanf("%d%d",&n,&K);mn=n;
    for(int i=1,x,y,z;i<n;i++){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);add(y,x,z);
    }
    getroot(1,0,n);
    printf("%lld",calc(rt));
    return 0;
}

原文地址:https://www.cnblogs.com/de-compass/p/11253154.html

时间: 2024-08-09 13:24:45

cqyz oj | 树的分治 | 树形DP | 树的重心的相关文章

树形DP+树状数组 HDU 5877 Weak Pair

1 //树形DP+树状数组 HDU 5877 Weak Pair 2 // 思路:用树状数组每次加k/a[i],每个节点ans+=Sum(a[i]) 表示每次加大于等于a[i]的值 3 // 这道题要离散化 4 5 #include <bits/stdc++.h> 6 using namespace std; 7 #define LL long long 8 typedef pair<int,int> pii; 9 const double inf = 12345678901234

hdu-4118 Holiday&#39;s Accommodation(树形dp+树的重心)

题目链接: Holiday's Accommodation Time Limit: 8000/4000 MS (Java/Others)     Memory Limit: 200000/200000 K (Java/Others) Problem Description Nowadays, people have many ways to save money on accommodation when they are on vacation.One of these ways is exc

2014 Super Training #9 E Destroy --树的直径+树形DP

原题: ZOJ 3684 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3684 题意: 给你一棵树,树的根是树的中心(到其他点的最远距离最小).现在你要破坏所有叶子节点到根节点的连通,每条边破坏都需要一定能量.你有一个能量为power的武器,能破坏能量小于等于power的任何路.求最少需要的power. 解法参考博客:http://blog.csdn.net/gzh1992n/article/details/86511

(中等) HDU 5293 Tree chain problem,树链剖分+树形DP。

Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n.There are m chain on the tree, Each chain has a certain weight. Coco would like to pick out some chains any two of which do not share common vertices.Find out the

HDOJ 5293 Tree chain problem LCA+树链剖分+树形DP

[题意] 给定一颗树上的几条链和每条链的权值,求能取出的不含有公共节点的链的最大权值.... [解] 预处理每条链的lca 树形DP, d[i]表示取到这个节点时可以得到的最大值 , sum[i]=sigma( d[k] | k 是i的子节点) 如果不取i  d[i]=sum[i] 如果取i , e是lca为i的链则 d[i]=max(d[i],e的权值+sigma(sum[k])-sigma(d[k]))  k为树链上的点 可以用树链剖分+树装数组在nlogn的时间复杂度内求链上的值 Tree

POJ 1849 Two (树形dp 树的直径 两种方法)

Two Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 1232   Accepted: 619 Description The city consists of intersections and streets that connect them. Heavy snow covered the city so the mayor Milan gave to the winter-service a list of st

[模拟赛10.12] 老大 (二分/树的直径/树形dp)

[模拟赛10.12] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n ? 1 条边的无向连通图),由于新建的办公室太大以至于要将奖杯要分放在两个不同的地方以便同学们丢硬币进去开光,OB 想请你帮帮他看看奖杯放在哪两个办公室使得在任意一个在劳模办公室做题的小朋友能最快地找到奖杯来开光. 一句话题意:给出一个 n 个点的树,在两个合适且不同的点放上奖杯,使得每个点到最近的奖杯距离最大值最小. 输入

肥宅快乐树 换根+树形DP/dfs

肥宅快乐树是一棵神秘而巨大的树,它长有许多枝条和节点,每条枝连接树中两个节点,每个节点上都长有一瓶肥宅快乐水. 何老板是肥宅快乐水的资深爱好者.历经艰难,他终于找到了这棵传说中的快乐树.他想要获取树上所有的快乐水,迫不及待地想从树根往树上爬. 每经过一条树枝都会耗费一定体力.而且快乐树自带防御功能,即每条枝上都有一个一次性陷阱,一旦踏上该枝,何老板就会被立即弹射回地面,他得重新从根往上爬. (注1:一次性陷阱是指,陷阱只在第一次经过该枝时有效) (注2:从i号点回到i的父亲节点,不耗费体力) 每

树形DP 树的最小支配集,最小点覆盖与最大独立集

最小支配集: 从V中选取尽量少的点组成一个集合,让V中剩余的点都与取出来的点有边相连. (点) 最小点覆盖: 从V中选取尽量少的点组成一个集合V1,让所有边(u,v)中要么u属于V1,要么v属于V1 (边) 最大独立集: 从V中选取尽量多的点组成一个集合,让这些点中间没有边项链,也就是说对于任何一条边,u,v不能同时属于集合V1. 1.贪心算法 首先选取一个点为根节点,求出所有节点对应的DFS序列,按照所得序列反向进行贪心,这样保证对于每个点来说,当子树都被处理过之后才会处理该节点 int p[