传送门

良心解析

其实以前在求某段序列上的区间统计问题时就碰到过类似于这样的思想。

当时的区间统计问题思路大致是这样:

选取一个点作为中间点,从这个点的左边和右边统计出满足条件的点对。然后当前的中间点就可以删去了,接着递归统计左右两个区间的方案数。

其实这就是个分治和分类讨论的思想。

满足要求的解无非就是这两种:

1.区间包含中间点(也就是两端点在中间点的左右两边)

2.区间不包含中间点(也就是两个端点都在中间点的左边或右边)

所以正确性显而易见

淀粉质就是把这种思想应用于树上的路径统计上,选取一个点作为根,然后统计经过这个点的路径数,端点一定是在子树中的,

不过这会遇到一个问题,就是两个端点会在同一颗子树中,这就需要再统计子树中的答案再减去。接着再递归求解每一颗子树。

最后一个问题就是根节点的选取,需要选重心,防止出现链导致时间复杂度退化的情况。

其次是统计答案的方法,对于此题来说有个神奇的nlogn的方法,上面的链接中已经给出。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100001
#define INF ~(1 << 31) using namespace std; int n, K, cnt, root, tot, ans;
int head[N], to[N], nex[N], val[N], f[N], size[N], deep[N], d[N];
bool vis[N];
//f[i]表示节点i的最大子树的大小
//deep[i]表示节点i到根的距离
//vis[i] == 1 表示该节点删除 inline int read()
{
int x = 0, f = 1;
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
} inline void add(int x, int y, int z)
{
to[cnt] = y;
val[cnt] = z;
nex[cnt] = head[x];
head[x] = cnt++;
} //获取当前块的重心
inline void getroot(int u, int fa)
{
int i, v;
f[u] = 0;
size[u] = 1;
for(i = head[u]; ~i; i = nex[i])
{
v = to[i];
if(v == fa || vis[v]) continue;
getroot(v, u);
size[u] += size[v];
f[u] = max(f[u], size[v]);
}
f[u] = max(f[u], tot - size[u]);
if(f[u] < f[root]) root = u;
} //获取当前块中所有的点到根的距离
inline void getdeep(int u, int fa)
{
int i, v;
deep[++deep[0]] = d[u];
for(i = head[u]; ~i; i = nex[i])
{
v = to[i];
if(v == fa || vis[v]) continue;
d[v] = d[u] + val[i];
getdeep(v, u);
}
} //求解经过当前根的满足条件的路径的方案数
inline int cal(int u, int now)
{
int l, r, ret = 0;
d[u] = now;
deep[0] = 0;
getdeep(u, 0);
sort(deep + 1, deep + deep[0] + 1);
l = 1, r = deep[0];
while(l < r)
if(deep[l] + deep[r] <= K) ret += r - l++;
else r--;
return ret;
} //递归求解每一块
inline void work(int u)
{
int i, v;
vis[u] = 1;
ans += cal(u, 0);
for(i = head[u]; ~i; i = nex[i])
{
v = to[i];
if(vis[v]) continue;
//因为在求解的时候会遇到这种情况:经过当前根的满足条件的路径的两端点在同一颗子树中,这样的路径也会统计到答案中
//然而并不合法,所以需要遍历每一颗子树,减去每一颗子树中满足条件的路径
ans -= cal(v, val[i]);
tot = size[v];
root = 0;
getroot(v, u);
work(root);
}
} int main()
{
int i, x, y, z;
while(~scanf("%d %d", &n, &K))
{
if(!n && !K) break;
cnt = root = ans = 0;
memset(vis, 0, sizeof(vis));
memset(head, -1, sizeof(head));
for(i = 1; i < n; i++)
{
x = read();
y = read();
z = read();
add(x, y, z);
add(y, x, z);
}
tot = n;
f[0] = INF;
getroot(1, 0);
work(root);
printf("%d\n", ans);
}
return 0;
}

  

[POJ1741]Tree(点分治模板)的更多相关文章

  1. POJ1741 Tree 树分治模板

    http://poj.org/problem?id=1741   题意:一棵n个点的树,每条边有距离v,求该树中距离小于等于k的点的对数.   dis[y]表示点y到根x的距离,v代表根到子树根的距离 ...

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

    题意:求树中点对距离<=k的无序点对个数. 解题关键:树上点分治,这个分治并没有传统分治的合并过程,只是分成各个小问题,并将各个小问题的答案相加即可,也就是每层的复杂度并不在合并的过程,是在每层 ...

  3. POJ - 1741 - Tree - 点分治 模板

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

  4. [POJ1741]Tree(点分治)

    树分治之点分治入门 所谓点分治,就是对于树针对点的分治处理 首先找出重心以保证时间复杂度 然后递归处理所有子树 对于这道题,对于点对(u,v)满足dis(u,v)<=k,分2种情况 路径过当前根 ...

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

    可以说是点分治第一题,之前那道的点分治只是模模糊糊,做完这道题感觉清楚了很多,点分治可以理解为每次树的重心(这样会把数分为若干棵子树,子树大小为log级别),然后统计包含重心的整个子树的值减去各个子树 ...

  6. bzoj 1468 Tree(点分治模板)

    1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1527  Solved: 818[Submit][Status][Discuss] ...

  7. [洛谷P4178] Tree (点分治模板)

    题目略了吧,就是一棵树上有多少个点对之间的距离 \(\leq k\) \(n \leq 40000\) 算法 首先有一个 \(O(n^2)\) 的做法,枚举每一个点为起点,\(dfs\) 一遍可知其它 ...

  8. POJ1741 tree (点分治模板)

    题目大意: 给一棵有 n 个顶点的树,每条边都有一个长度(小于 1001 的正整数).定义 dist(u,v)=节点 u 和 v 之间的最小距离.给定一个整数 k,对于每一对 (u,v) 顶点当且仅当 ...

  9. POJ1741 Tree + BZOJ1468 Tree 【点分治】

    POJ1741 Tree + BZOJ1468 Tree Description Give a tree with n vertices,each edge has a length(positive ...

  10. poj1741 Tree(点分治)

    题目链接:http://poj.org/problem?id=1741 题意:求树上两点之间距离小于等于k的点对的数量 思路:点分治模板题,推荐一篇讲的非常好的博客:https://blog.csdn ...

随机推荐

  1. ios has denied the launch request.

    ios has denied the launch request. You can choose either of the two ways. Solution 1: Open System Pr ...

  2. Yii2.0数据格式器

    平时我们在写代码中,总是要写一个单独的文件来全局处理常用的数据格式.Yii2.0却很人性化,为我们内置了一套数据格式器. 1.格式化日期和时间 Yii::$app->formatter-> ...

  3. Redis性能优化之redis.cnf配置参数

    redis调优总结 1.相应的参数调优 加内存2.redis使用结构调优3.使用合理的数据类型说明:redis存储的数据为redis hash(字符映射表) 单key多字段结构. 1)调整配置文件中配 ...

  4. leetcode 127 单词接龙

    给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度.转换需遵循如下规则: 每次转换只能改变一个字母. 转换过程中的中 ...

  5. dfs染色法判定二分图

    #include<iostream> #include<cstring> using namespace std; ][],color[],n; int dfs(int x,i ...

  6. hash 散列表

    一个字符串的hash值: •现在我们希望找到一个hash函数,使得每一个字符串都能够映射到一个整数上 •比如hash[i]=(hash[i-1]*p+idx(s[i]))%mod •字符串:abc,b ...

  7. tomcat性能优化 - 网络抄录

    tomcat默认参数是为开发环境制定,而非适合生产环境,尤其是内存和线程的配置,默认都很低,容易成为性能瓶颈. tomcat内存优化 linux修改TOMCAT_HOME/bin/catalina.s ...

  8. Linux C++/C开发所必需的一系列工具

    系统平台下的开发工具.开发环境各有不同.Linux C++/C开发所必需的一系列工具: 1. vi(vim)文本编辑器一个UNIX世界标准的文本编辑器,简约而强大,不论作为开发人员还是系统管理员,熟练 ...

  9. iptables 过滤字符串

    iptables 过滤字符串 1. 开启iptables iptables -P OUTPUT ACCEPT       ###允许输出链 service iptables save          ...

  10. [LUOGU] P1551 亲戚

    题目背景 若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系. 题目描述 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚.如 ...