题目链接:http://poj.org/problem?id=1741

题意:

给定一棵包含$n$个点的带边权树,求距离小于等于K的点对数量

题解:

显然,枚举所有点的子树可以获得答案,但是朴素发$O(n^2logn)$算法会超时,

利用树的重心进行点分治可以将$O(n^2logn)$的上界优化为近似$O(nlogn)$

足以在1000ms的测试时间内通过

具体原理参考注释

#include<iostream>
#include<map>
#include<string>
#include<cstring>
#include<vector>
#include<algorithm>
#include<set>
#include<sstream>
#include<cstdio>
#include<cmath>
#include<climits>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define IO ios::sync_with_stdio(false)
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
#define forn(x,i) for(int i=head[x];i;i=e[i].next)
#define show(x) cout<<#x<<"="<<x<<endl
#define showa(a,b) cout<<#a<<'['<<b<<"]="baidu<a[b]<<endl
#define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
#define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
using namespace std;
const int maxn=1e6+10,maxm=2e6+10;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const double PI=acos(-1.0);
//head
int casn,n,m,k;
ll val[maxn],dis[maxn],ans,maxt,dfn;
int deep[maxn],vis[maxn],size[maxn];
int dp[maxn],allnode;
struct node {int to,next;ll cost;}e[maxm];int head[maxn],nume;//静态链表存图
void add(int a,int b,ll c){e[++nume]=(node){b,head[a],c};head[a]=nume;}
int mid;
void init(){//初始化
memset(head,0,sizeof head);
memset(dis,0,sizeof dis);
memset(vis,0,sizeof vis);
nume=0;
}
void getmid(int now,int pre){//dfs求树的重心
size[now]=1;//当前点为根,其子树的节点数
for(int i=head[now];i;i=e[i].next){
int to=e[i].to;
if(to==pre||vis[to]) continue;
getmid(to,now);//递归计算子树的大小
size[now]+=size[to];
}
dp[now]=max(size[now],allnode-size[now]);//dp[i]表示以i为根建立子树的时候,最大的子树大小
if(maxt>dp[now]){//maxt为最大的子树大小
maxt=dp[now];
mid=now;
}
}
void dfs(int now,int pre){//计算深度
deep[++dfn]=dis[now];
for(int i=head[now];i;i=e[i].next){
int to=e[i].to,cost=e[i].cost;
if(to==pre||vis[to]) continue;
dis[to]=dis[now]+cost;
dfs(to,now);
}
}
int cal(int rt,int len){//计算rt为根的子树中,深度之和>=k的点对数量
dis[rt]=len,dfn=0;
dfs(rt,0);//以rt为根,dfs计算其子树中所有点的深度
sort(deep+1,deep+dfn+1);
int res=0;
for(int l=1,r=dfn;l<r;){//排序后从两端向中间逼近,总复杂度nlogn
if(deep[l]+deep[r]<=k){
res+=r-l;
l++;
}else r--;
}
return res;
}
void dc(int rt){//分治以rt为根的子树
vis[rt]=1;
ans+=cal(rt,0);//初步计算以rt为根子树答案,包含重复情况
for(int i=head[rt];i;i=e[i].next){
int to=e[i].to;
if(vis[to]) continue;
ans-=cal(to,e[i].cost);//以子节点为根的子树,设置其距离下界为len,对于其子树而言,如果距离减少子树到rt的距离,依然成立的话,必然会被重复计算
allnode=size[to];
mid=0,maxt=INF;
getmid(to,rt);//寻找以rt为根的子树的重心
dc(mid);//以子树重心为树上点分治的起点,保证总复杂度为n(logn)^2级别
} }
int main() {
//#define test
#ifdef test
auto _start = chrono::high_resolution_clock::now();
freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
IO;
while(cin>>n>>k,n+k){
init();
int a,b,c;
rep(i,2,n){
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
mid=ans=0;
allnode=n,maxt=INF;
getmid(1,0);
dc(mid);
cout<<ans<<endl;
}
#ifdef test
auto _end = chrono::high_resolution_clock::now();
cerr << "elapsed time: " << chrono::duration<double, milli>(_end - _start).count() << " ms\n";
fclose(stdin);fclose(stdout);system("out.txt");
#endif
return 0;
}

  

POJ 1741 Tree 树上点分治的更多相关文章

  1. poj 1741 Tree(点分治)

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 15548   Accepted: 5054 Description ...

  2. POJ 1741 Tree (树分治入门)

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 8554   Accepted: 2545 Description ...

  3. POJ 1741 Tree (点分治)

                                                                        Tree Time Limit: 1000MS   Memory ...

  4. POJ 1741 Tree 树的分治

    原题链接:http://poj.org/problem?id=1741 题意: 给你棵树,询问有多少点对,使得这条路径上的权值和小于K 题解: 就..大约就是树的分治 代码: #include< ...

  5. POJ 1741 Tree【树分治】

    第一次接触树分治,看了论文又照挑战上抄的代码,也就理解到这个层次了.. 以后做题中再慢慢体会学习. 题目链接: http://poj.org/problem?id=1741 题意: 给定树和树边的权重 ...

  6. poj 1741 Tree (树的分治)

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 30928   Accepted: 10351 Descriptio ...

  7. POJ 1741 Tree 树的分治(点分治)

    题目大意:给出一颗无根树和每条边的权值,求出树上两个点之间距离<=k的点的对数. 思路:树的点分治.利用递归和求树的重心来解决这类问题.由于满足题意的点对一共仅仅有两种: 1.在以该节点的子树中 ...

  8. POJ 1741 Tree ——(树分治)

    思路参考于:http://blog.csdn.net/yang_7_46/article/details/9966455,不再赘述. 复杂度:找树的重心然后分治复杂度为logn,每次对距离数组dep排 ...

  9. POJ 1741 Tree 求树上路径小于k的点对个数)

                                                                                                 POJ 174 ...

随机推荐

  1. Dubbo简易控制中心安装

    1. zookeeper安装(单机版):http://www.cnblogs.com/wangfajun/p/5251159.html  √ 2. dubbo管控台安装(zookeeper单机版):h ...

  2. HDU 1018(阶乘位数 数学)

    题意是求 n 的阶乘的位数. 直接求 n 的阶乘再求其位数是不行的,开始时思路很扯淡,想直接用一个数组存每个数阶乘的位数,用变量 tmp 去存 n 与 n - 1 的阶乘的最高位的数的乘积,那么 n ...

  3. Noisy Channel模型纠正单词拼写错误

    本文介绍 Stanford<From Languages to Information>课程中讲到的 单词拼写错误 纠正.背后的数学原理主要是贝叶斯公式.单词拼写错误纠正主要涉及到两个模型 ...

  4. Java集合操作精华总结

    一.Set1.HashSet boolean add(E e) 添加 访问 boolean remove(E e) 删除 Iterator<E> iterator 遍历 int size( ...

  5. IScroll5要防止重复加载

    增加一个判断条件,ajax未返回前,设置为true,返回前设置为false,只有为false下才能够出发加载数据事件效果好很多.

  6. 使用 CROSS APPLY 与 OUTER APPLY 连接查询

    Ø  前言 日常开发中遇到多表查询时,首先会想到 INNER JOIN 或 LEFT OUTER JOIN 等等,但是这两种查询有时候不能满足需求.比如,左表一条关联右表多条记录时,我需要控制右表的某 ...

  7. 12个实用的 JavaScript 框架分享给前端开发者

    JavaScript库是预先编写的 JavaScript 工具代码,让开发者可以更容易开发 JavaScript 应用.这个列表我们列出了2017年1月份功能丰富的 JavaScript 库,可以帮助 ...

  8. 【noip 2016】提高组

    D1T1.玩具谜题 题目链接 直接模拟就好了……water. #include<cstdio> int n,m,a,s,ans; ];]; int main() { scanf(" ...

  9. [C++]PAT乙级1011. A+B和C (15/15)

    /* 1011. A+B和C (15) 给定区间[-2^31, 2^31]内的3个整数A.B和C,请判断A+B是否大于C. 输入格式: 输入第1行给出正整数T(<=10),是测试用例的个数.随后 ...

  10. 【Git】在GitHub或OSChina上新建项目后,如何在本地第一次push代码到服务器

    场景1:将本地代码push到远程仓库上的master主分支 #初始化git,执行init命令后,默认新建本地分支master git init #关联远程仓库 git remote add origi ...