洛谷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和连 ...
随机推荐
- HTML5利用canvas,把多张图合并成一张图片
需求分析,根据当前网页中的几张图片,在手机上长按,保存图片到相册或者发送给好友. drawCanvas(){ var self = this; var imgsrcArray = [ require( ...
- MongoDB的存储结构及对空间使用率的影响
MongoDB的存储结构及对空间使用率的影响 使用MongoDB一段时间的同学肯定会发现,MongoDB往往会占用比实际数据大小多不少空间的问题.如果利用db.stats()命令去查看,会发现Mong ...
- mssql sqlserver 快速表备份和表还原的方法
摘要: 在sqlserver维护中,我们偶尔需要运行一些sql脚本对数据进行相关修改操作,在数据修改前我们必须对表数据进行备份来避免出现异常时,可以快速修复数据, 下文讲述sqlserver维护中,快 ...
- MPP架构海量数据分析仓库——Greenplum介绍
一.Greenplum背景 时间回到2002年,互联网行业经过近10年的发展,数据量正处于快速增长期: 1.传统的主机计算模式在海量数据面前,除了造价昂贵外,在CPU计算和IO吞吐上不能满足海量数据的 ...
- python3 set(集合)
add(增加元素) name = set(['Tom','Lucy','Ben']) name.add('Juny') print(name) #输出:{'Lucy', 'Juny', 'Ben', ...
- 删除网络中的本地连接*x等
1.运行-regedit 打开注册表 定位到 HKEY_LOCAL_MACHINE SYSTEM CurrentControlSet Control Network {4D36E972-E325-11 ...
- 在android中进行单元测试的步骤
若不知道怎么配上面两个参数
- 给Integer对象加锁的错误方式
package com.thread.test; public class BadLockOnInteger implements Runnable { public static Integer i ...
- Interrupt中断线程注意点
首先我们要明确,线程中断并不会使线程立即退出,而是发送一个通知,告知目标线程你该退出了,但是后面如何处理,则完全有目标线程自行决定. 这就是和stop()不一样的地方,stop执行后线程会立即终止,这 ...
- Python开发【内置模块篇】configparser
生成配置文件 import configparser config = configparser.ConfigParser() config[', 'Compression': 'yes', ', ' ...