洛谷P4178 Tree (算竞进阶习题)
点分治
还是一道点分治,和前面那道题不同的是求所有距离小于等于k的点对。
如果只是等于k,我们可以把重心的每个子树分开处理,统计之后再合并,这样可以避免答案重复(也就是再同一个子树中出现路径之和为k的点)
但是对于这道题,如果我们还要这样求的话显然是会超时的,意外要枚举所有点的话有点勉强 。。。
考虑一次把重心的子树全部遍历,统计到重心的距离,放进数组中,排序。然后我们可以用指针对撞的方法,用l,r两个指针分别从前后开始扫描。
容易发现,当指针再l的位置时,如果我们记录距离排好序的数组rd[l] + rd[r] <= k, 那么我们可以直接统计答案(r-l),之后l++,如果rd[l] + rd[r] > k,那么我们就让r--。
但是这样并不是正确的答案,因为这样我们再同一个子树中距离小于等于k的点也被算进去了,我们需要再下一次分治处理子树重心之前减去他。
具体方法就是,我们再重心rt的子树u中,去找距离之和为k-2*dis(rt, u)的点对,答案减去它就行了。有个小技巧,我们可以直接把u距离自己的距离设置成dis(rt,u),这样的话我们还是可以在他的子树中查找距离为k的点对,实际上就是默认给每个点到u的距离都加了dis(rt,u)
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define full(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
inline int lowbit(int x){ return x & (-x); }
inline int read(){
int X = 0, w = 0; char ch = 0;
while(!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
return w ? -X : X;
}
inline int gcd(int a, int b){ return a % b ? gcd(b, a % b) : b; }
inline int lcm(int a, int b){ return a / gcd(a, b) * b; }
template<typename T>
inline T max(T x, T y, T z){ return max(max(x, y), z); }
template<typename T>
inline T min(T x, T y, T z){ return min(min(x, y), z); }
template<typename A, typename B, typename C>
inline A fpow(A x, B p, C lyd){
A ans = 1;
for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
return ans;
}
const int N = 60005;
int n, k, cnt, ans, rt, sum, tot, res, head[N], size[N], dis[N], rd[N];
bool vis[N];
struct Edge { int v, next, w; } edge[N<<2];
void addEdge(int a, int b, int c){
edge[cnt].v = b, edge[cnt].w = c, edge[cnt].next = head[a], head[a] = cnt ++;
}
void dfs(int s, int fa){
int mp = 0;
size[s] = 1;
for(int i = head[s]; i != -1; i = edge[i].next){
int u = edge[i].v;
if(u == fa || vis[u]) continue;
dfs(u, s);
size[s] += size[u];
mp = max(mp, size[u]);
}
mp = max(mp, sum - size[s]);
if(mp < ans) ans = mp, rt = s;
}
void getDis(int s, int fa){
rd[++tot] = dis[s];
for(int i = head[s]; i != -1; i = edge[i].next){
int u = edge[i].v;
if(u == fa || vis[u]) continue;
dis[u] = dis[s] + edge[i].w;
getDis(u, s);
}
}
int calc(int s, int w){
dis[s] = w;
tot = 0, getDis(s, 0);
sort(rd + 1, rd + tot + 1);
int l = 1, r = tot, ret = 0;
while(l <= r) rd[l] + rd[r] <= k ? (ret += r - l, l ++) : (r --);
return ret;
}
void solve(int s){
res += calc(s, 0);
vis[s] = true;
for(int i = head[s]; i != -1; i = edge[i].next){
int u = edge[i].v;
if(vis[u]) continue;
res -= calc(u, edge[i].w);
ans = INF, sum = size[u];
dfs(u, 0), solve(rt);
}
}
int main(){
full(head, -1);
n = read();
for(int i = 0; i < n - 1; i ++){
int u = read(), v = read(), c = read();
addEdge(u, v, c), addEdge(v, u, c);
}
k = read();
ans = INF, sum = n;
dfs(1, 0), solve(rt);
printf("%d\n", res);
return 0;
}
洛谷P4178 Tree (算竞进阶习题)的更多相关文章
- 点分治模板(洛谷P4178 Tree)(树分治,树的重心,容斥原理)
推荐YCB的总结 推荐你谷ysn等巨佬的详细题解 大致流程-- dfs求出当前树的重心 对当前树内经过重心的路径统计答案(一条路径由两条由重心到其它点的子路径合并而成) 容斥减去不合法情况(两条子路径 ...
- BZOJ 3261 最大异或和(算竞进阶习题)
可持久化Trie 需要知道一个异或的特点,和前缀和差不多 a[p] xor a[p+1] xor....xor a[n] xor x = a[p-1] xor a[n] xor x 所以我们把a[1. ...
- POJ1471 Tree/洛谷P4178 Tree
Tree P4178 Tree 点分治板子. 点分治就是直接找树的重心进行暴力计算,每次树的深度不会超过子树深度的\(\frac{1}{2}\),计算完就消除影响,找下一个重心. 所以伪代码: voi ...
- 洛谷P2045 K方格取数(算竞进阶习题)
费用流 又是一道网络流的模型,对于这种费用与经过次数有关的边,我们经常把边拆成多条,比如这个题,第一次费用是x,第二次是0,我们就可以先把点拆成入点和出点,入点和出点又连两条边,第一条容量为1,费用为 ...
- 洛谷P1092 虫食算(算竞进阶习题)
模拟+dfs 这个题就三行,搜索的话我们从右向左,从上到下.. 如果是在1,2行我们就直接枚举0-n所有数,但是到了第三行,最直接的就是填上这一列上前两行的数的和modN,在此基础上判断该填的数有没有 ...
- 洛谷P1074 靶形数独(算竞进阶习题)
还是数独.. 比上一个多了个分数矩阵,其实没什么差别,但是数据好像水了许多... #include <bits/stdc++.h> #define INF 0x3f3f3f3f using ...
- BZOJ 1912 巡逻(算竞进阶习题)
树的直径 这题如果k=1很简单,就是在树的最长链上加个环,这样就最大化的减少重复的路程 但是k=2的时候需要考虑两个环的重叠部分,如果没有重叠部分,则和k=1的情况是一样的,但是假如有重叠部分,我们可 ...
- POJ 2449 Remmarguts' Date (算竞进阶习题)
A* + dijkstra/spfa 第K短路的模板题,就是直接把最短路当成估价函数,保证估价函数的性质(从当前状态转移的估计值一定不大于实际值) 我们建反图从终点跑最短路,就能求出从各个点到终点的最 ...
- POJ 3667 Hotel(算竞进阶习题)
线段树区间染色 题目要求最大的连续段的左端点,我们在查询的时候返回最左端即可,注意查找顺序,应该从左到右!! 另外这类染色的push_down其实比较简单,直接染成上一层的标记即可 push_up和连 ...
随机推荐
- 【Dojo 1.x】笔记1 入门
Dojo是个框架 ,是个js框架,现在除了这一点什么都不知道,就这么上手了. ps:不建议Web初学者看我的笔记,这个要有一定积累才能看. 在<body>标签内写这么一点代码: <b ...
- Arcgis瓦片--数据获取
Arcgis的二维地图瓦片有两种获取方式 1.在Arcmap中对配置好的地图进行切图,生成对应瓦片 2.使用第三方的地图下载器,直接下载,导出成arcgis瓦片格式即可使用. 备注:这里主要介绍第二种 ...
- JVM远程调试功能
有时候想调试线上的程序 可以启用远程调试功能 在本地调试远程代码. 远程JVM启用调试模式 /usr/local/jdk/bin/java -server -Xms256m -Xmx256m -XX: ...
- MySQL InnoDB下关于MVCC的一个问题的分析
这个是网友++C++在群里问的一个关于MySQL的问题,本篇文章实验测试环境为MySQL 5.6.20,事务隔离级别为REPEATABLE-READ ,在演示问题前,我们先准备测试环境.准备一个测 ...
- 基于JavaMail的Java邮件发送:简单邮件发送
使用Java应用程序发送 E-mail 十分简单,但是首先你应该在你的机器上安装 JavaMail API 和Java Activation Framework (JAF) . 您可以从 Java 网 ...
- Java 集合系列(四)—— ListIterator 源码分析
以脑图的形式来展示Java集合知识,让零碎知识点形成体系 Iterator 对比 Iterator(迭代器)是一种设计模式,是一个对象,用于遍历集合中的所有元素. Iterator 包含四个方法 ...
- MongoDB在Linux系统下的安装与启动
Mongodb介绍 MongoDB是一个开源文档数据库,提供高性能,高可用性和自动扩展,官方文档:https://docs.mongodb.com/manual/introduction/ Mongo ...
- springboot项目屏蔽mq或者mongodb的监控日志输出
最近写项目,用的是springboot,其中用到了rabbitmq和mongodb,配置完成 项目启动后,会输出如下日志: mongodb和mq的检测,会一直打印日志,这样会影响开发人员的测试. 如何 ...
- 【Linux基础】grep命令
1.简介 grep是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来. 命令格式:grep [option] pattern file 2.常用参数与举例: -e : 使用P ...
- poj 2255 Tree Recovery(求后序遍历,二叉树)
版权声明:本文为博主原创文章,未经博主同意不得转载.vasttian https://blog.csdn.net/u012860063/article/details/37699219 转载请注明出处 ...