第一次接触树分治,看了论文又照挑战上抄的代码,也就理解到这个层次了。。

以后做题中再慢慢体会学习。


题目链接:

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

题意:

给定树和树边的权重,求有多少对顶点之间的边的权重之和小于等于K。

分析:

树分治。

直接枚举不可,我们将树划分成若干子树。

那么两个顶点有两种情况:

  1. u,v属于同一子树的顶点对
  2. u,v属于不同子树的顶点对

第一种情况,对子树递归即可求得。

第二种情况,从u到v的路径必然经过了顶点s,只要先求出每个顶点到s的距离再做统计即可。(注意在第二种情况中减去第一种重复计算的部分)

当树退化成链的形式时,递归的深度则退化为O(n),所以选择每次都找到树的重心作为分隔顶点。重心就是删掉此结点后得到的最大子树的顶点数最少的顶点,删除重心后得到的所有子树顶点数必然不超过n/2。

查找重心的时候,假设根为v,先在v的子树中找到一个顶点,使删除该顶点后的最大子树的顶点数最少,然后考虑删除v的情况,获得最大子树的顶点数。

两者选择最小的一个,此时选中的顶点即为重心。

递归的每一层都做了排序O(nlogn),递归深度O(logn),总体时间复杂度O(nlog2n)。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define sa(a) scanf("%d", &a)
#define mem(a,b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 1e4 + 5, oo = 0x3f3f3f3f;
int cnt[maxn], vis[maxn];
int K, ans;
struct EDGE{int to; int length;int next;};
int head[maxn];
EDGE edge[maxn * 2];
typedef pair<int, int>pii;
int tot = 0;
void addedge(int u, int v, int l)
{
edge[tot].to = v;
edge[tot].length = l;
edge[tot].next = head[u];
head[u] = tot++;
edge[tot].to = u;
edge[tot].length = l;
edge[tot].next = head[v];
head[v] = tot++;
}
int countsubtree(int v, int p)
{
int ans = 1;
for(int i = head[v]; i != -1; i = edge[i].next){
int w = edge[i].to;
if(w == p||vis[w]) continue;
ans += countsubtree(w, v);
}
return cnt[v] = ans;
}
pii findc(int v, int p, int t)
{
pii res = pii(oo, 0);
int s = 1, m = 1;
for(int i = head[v]; i != -1; i = edge[i].next){
int w = edge[i].to;
if(w == p || vis[w]) continue;
res = min(res, findc(w, v, t));
m = max(m, cnt[w]);
s += cnt[w];
}
m = max(m, t - s);
res = min(res, pii(m, v));
return res;
}
void findpath(int v, int p, int d, vector<int>&ds)
{
ds.push_back(d);
for(int i = head[v]; i != -1; i = edge[i].next){
int w = edge[i].to;
if(w == p || vis[w]) continue;
findpath(w, v, d +edge[i].length, ds);
}
}
int count_pair(vector<int>&ds)
{
int res = 0;
sort(ds.begin(), ds.end());
int j = ds.size() - 1;
int i = 0;
while(i < j){
while(j > i &&ds[i] + ds[j] > K) j--;
res += j - i;
i++;
}
return res;
/*
int j = ds.size();
for(int i = 0; i < ds.size(); i++){
while(j > 0 && ds[i] + ds[j - 1] > K) j--;
res += j - (j > i?1:0);
}
return res / 2;*/
}
void solve(int v)
{
vector<int>ds;
countsubtree(v, -1);
int s = findc(v, -1, cnt[v]).second;
vis[s] =true;
//(1)
for(int i = head[s]; i != -1; i = edge[i].next){
int w = edge[i].to;
if(vis[w]) continue;
solve(w);
}
//(2)
ds.push_back(0);
for(int i = head[s]; i != -1; i = edge[i].next){
int w = edge[i].to;
if(vis[w]) continue;
vector<int>ts;
findpath(w, s, edge[i].length, ts);
ans -= count_pair(ts);
ds.insert(ds.end(), ts.begin(), ts.end());
}
vis[s] = false;
ans += count_pair(ds);
}
void init()
{
tot = 0;
ans = 0;
mem(head, -1);
mem(vis, 0);
mem(cnt, 0);
}
int main (void)
{
int n;
while(scanf("%d%d", &n, &K)== 2 && n + K){
int u, v, l;
init();
for(int i = 0; i < n - 1; i++){
sa(u),sa(v),sa(l);
addedge(u, v, l);
}
solve(1);
printf("%d\n", ans);
}
return 0;
}

POJ 1741 Tree【树分治】的更多相关文章

  1. POJ 1741.Tree 树分治 树形dp 树上点对

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 24258   Accepted: 8062 Description ...

  2. POJ 1741 Tree 树分治

    Tree     Description Give a tree with n vertices,each edge has a length(positive integer less than 1 ...

  3. poj 1741 Tree (树的分治)

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

  4. poj 1744 tree 树分治

    Tree Time Limit: 1000MS   Memory Limit: 30000K       Description Give a tree with n vertices,each ed ...

  5. POJ 1741 Tree ——点分治

    [题目分析] 这貌似是做过第三道以Tree命名的题目了. 听说树分治的代码都很长,一直吓得不敢写,有生之年终于切掉这题. 点分治模板题目.自己YY了好久才写出来. 然后1A了,开心o(* ̄▽ ̄*)ブ ...

  6. POJ 1741 Tree(树的点分治,入门题)

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 21357   Accepted: 7006 Description ...

  7. POJ 1741 Tree 树的分治

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

  8. Tree POJ - 1741【树分治】【一句话说清思路】

    因为该博客的两位作者瞎几把乱吹(" ̄︶ ̄)人( ̄︶ ̄")用彼此的智慧总结出了两条全新的定理(高度复杂度定理.特异根特异树定理),转载请务必说明出处.(逃 Pass:anuonei, ...

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

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

  10. POJ 1741 Tree(点分治点对<=k)

    Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Def ...

随机推荐

  1. check_http.c:312: error: ‘ssl_version’

    安装nagios-plugins-1.4.16,安装的过程中出现了错误,提示如下.check_http.c:312: error: ‘ssl_version’ undeclared (first us ...

  2. Sql Server 2012 分页方法分析(offset and fetch)

    最近在分析 Sql Server 2012 中 offset and fetch 的新特性,发现 offset and fetch 无论语法的简洁还是功能的强大,都是相当相当不错的.其中  offse ...

  3. 使用windows的fsutil命令创建指定大小及类型的测试文件

    在软件测试中,对于上传.下载一类功能常常需要用不同大小的文件进行测试. 使用Windows命令fsutil可以生成任意大小.任意类型文件. C:\Users\axia\fsutil file crea ...

  4. jmeter+ant+jenkins

    前提:需要先配置下面两个环境,严格按照本人的配置去配,要不然后面你会看不懂 (1)ant+jmeter集成:http://blog.csdn.net/qq_23101033/article/detai ...

  5. 李开复:AlphaGo 若打败了世界冠军,意味着什么?

    创新工场董事长李开复在知乎就AlphaGo与李世石的人机大战发表了自己看法,他认为四个月前的AlphaGo击败李世石基本不可能,不过这四个月AlphaGo进步很多,比赛应该很精彩.但是,无论这次结果如 ...

  6. GPU、CPU的异同

    一.概念 CPU(Center Processing Unit)即中央处理器,GPU(Graphics Processing Unit)即图形处理器. 二.CPU和GPU的相同之处 两者都有总线和外界 ...

  7. Vue.js Extension Pack 和 jsconfig.json 可以定位跳转到@开头的路径等自定义路径

    Vue.js Extension Pack | vsCode插件 可以定位跳转到@开头的路径等自定义路径 webpack自定义别名后,VScode路径提示问题 //tsconfig.json 或者 j ...

  8. postman设置环境变量、全局变量

    讲postman环境变量设置之前,先讲一个小插曲,环境变量.全局变量的区别在于Globals,只能用一组,而Environmen可以设置多组,所以我更喜欢设置环境变量 1.环境变量-Environme ...

  9. windows定时执行python脚本

    from:http://blog.csdn.net/Gpwner/article/details/77882131

  10. Web前端技术体系大全搜索

    一.前端技术框架 1.Vue.js 官网:https://cn.vuejs.org/ Vue CLI:https://cli.vuejs.org/ 菜鸟教程:http://www.runoob.com ...