- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
点分治适合处理大规模的树上路径信息问题.
给定一棵 \(n\) 个点树和一个整数 \(k\) ,求树上两点间的距离小于等于 \(k\) 的点对有多少.
对于这个题,如果我们进行 \(O_{n^3}\) 搜索,那只要 \(n\) 一大,铁定超时。 所以,我们要用一个更优秀的解法,这就是我们的点分治。 淀粉质可好吃了 。
typedef pair<int, int> pii;
const int N = 4e4 + 10;
int n, k, rt, ans, sum;
int siz[N], maxp[N], dis[N], ok[N];
bool vis[N];
vector<pii> son[N];
n : 点数; k : 限定距离; rt : 根节点; sum : 总结点数(找重心要用到); siz : 子树大小; maxp : 最大的子树的大小; dis : 每个节点到根节点的距离; ok : 栈; vis : 标记; son : 存图.
为什么是找重心? 其所有的子树中最大的子树节点数最少,在所有点中,重心是最优的选择。 找到重心后,以重心为根开始操作.
void get_root(int u, int fat) {
siz[u] = 1;
maxp[u] = 0;
for (pii it : son[u]) {
int v = it.first;
if (v == fat || vis[v]) continue;
get_root(v, u);
siz[u] += siz[v];
maxp[u] = max(maxp[u], siz[v]);
}
maxp[u] = max(maxp[u], sum - siz[u]);
if (maxp[u] < maxp[rt]) rt = u;
}
这里并不是很难.
对于每个根节点,我们进行搜索,会得到每个节点到根节点的距离。 我们现在要求出经过根节点的距离小于等于 \(k\) 的点对个数。 我们将所有点的距离从小到大排一个序,设置左右两个指针,如果左指针和右指针所指向的节点到根节点的距离小于等于 \(k\) ,则两个指针之间所有的节点到左指针所指向的节点的距离都小于等于 \(k\) ,与此同时 l ++ ,如果左右指针所指向的节点的距离之和大于 \(k\) ,那么右指针就要左移,即 -- r 。 然后我们对每个节点都这样搜一遍,将答案加出来,就可以轻松加愉快的切掉这个问题了 吗? 考虑一下,如果是下面这种情况 。
假设 \(k = 5\) ,那么以 \(1\) 为根节点时, \(4\) 与 \(5\) 很显然是符合的,我们将它加入答案。 然后,当我们又以 \(3\) 为根节点时, \(4\) 和 \(5\) 这个点对我们就又统计了一次。 有什么问题? 重复啦! 原因也很简单,因为 \(4\) 和 \(5\) 在同一个子树内,因此只要它们在这个大的树内符合要求,那么它们在它们的小子树内也一定符合要求,那么就一定会有重复,因此,利用容斥的原理,我们先求出总的答案,然后再减去重复的部分。 如何检验重复的部分呢? 我们发现它们共同经过了一条边 \(1 - 3\) ,所以我们再次搜索,这次直接初始化 dis[3] = 1 ,然后其他的依旧按照操作,最后如果他们的距离小于等于 \(k\) ,则这就是重复的部分,统计一下,最后减去即可。 减去之后,就在子树里找重心,设置新的根节点,开始新的答案统计,与此同时,我们要将原来的根节点打上标记,防止搜索范围离开了这个子树。 (或许这就是点“分治”的所在,搜完一个重心后,相当于把这个重心删除,然后就将一颗树分成多个互相之间没有联系的小子树,各自进行搜索) 。
int calc(int u, int val) {
ok[0] = 0;
dis[u] = val;
dfs(u, 0);
sort(ok + 1, ok + ok[0] + 1);
int cnt = 0, l = 1, r = ok[0];
while (l < r) {
if (ok[l] + ok[r] <= k) {
cnt += (r - l ++);
}
else {
r --;
}
}
return cnt;
}
void work(int u) {
ans += calc(u, 0);
vis[u] = 1;
for (pii it : son[u]) {
int v = it.first, w = it.second;
if (vis[v]) continue;
ans -= calc(v, w);
maxp[rt = 0] = sum = siz[v];
get_root(v, 0);
work(rt);
}
}
关于 sum = siz[v]; ,当我们再次找重心时,是要在这个子树中找重心,不能超出这个子树,因此要将总个数也设为 siz[v] .
这个,应该就没什么好说的了.
void dfs(int u, int fat) {
ok[++ ok[0]] = dis[u];
siz[u] = 1;
for (pii it : son[u]) {
int v = it.first, w = it.second;
if (v == fat || vis[v]) continue;
dis[v] = dis[u] + w;
dfs(v, u);
siz[u] += siz[v];
}
}
看到这,你已经看完了点分治的核心步骤,让我们把代码整合一下,去切掉这道模板题 Tree .
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 4e4 + 10;
int n, k, rt, ans, sum;
int siz[N], maxp[N], dis[N], ok[N];
bool vis[N];
vector<pii> son[N];
inline ll read() {
ll x = 0;
int fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
void get_root(int u, int fat) {
siz[u] = 1;
maxp[u] = 0;
for (pii it : son[u]) {
int v = it.first;
if (v == fat || vis[v]) continue;
get_root(v, u);
siz[u] += siz[v];
maxp[u] = max(maxp[u], siz[v]);
}
maxp[u] = max(maxp[u], sum - siz[u]);
if (maxp[u] < maxp[rt]) rt = u;
}
void dfs(int u, int fat) {
ok[++ ok[0]] = dis[u];
siz[u] = 1;
for (pii it : son[u]) {
int v = it.first, w = it.second;
if (v == fat || vis[v]) continue;
dis[v] = dis[u] + w;
dfs(v, u);
siz[u] += siz[v];
}
}
int calc(int u, int val) {
ok[0] = 0;
dis[u] = val;
dfs(u, 0);
sort(ok + 1, ok + ok[0] + 1);
int cnt = 0, l = 1, r = ok[0];
while (l < r) {
if (ok[l] + ok[r] <= k) {
cnt += (r - l ++);
}
else {
r --;
}
}
return cnt;
}
void work(int u) {
ans += calc(u, 0);
vis[u] = 1;
for (pii it : son[u]) {
int v = it.first, w = it.second;
if (vis[v]) continue;
ans -= calc(v, w);
maxp[rt = 0] = sum = siz[v];
get_root(v, 0);
work(rt);
}
}
int main() {
n = read();
for (int i = 1, u, v, w; i < n; ++ i) {
u = read(), v = read(), w = read();
son[u].push_back({v, w});
son[v].push_back({u, w});
}
k = read();
maxp[rt = 0] = sum = n;
get_root(1, 0);
work(rt);
printf("%d\n", ans);
return 0;
}
模板题 (大雾) 。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, ll> pil;
const int N = 1e4 + 5;
int n, m, sum, rt;
int q[N], siz[N], maxs[N], can[N], dis[N];
int tp[N];
bool ok[N], vis[N];
vector<pil> son[N];
bool cmp(int x, int y) {
return dis[x] < dis[y];
}
void get_root(int u, int fat, int tot) {
siz[u] = 1;
maxs[u] = 0;
for (auto [v, w] : son[u]) {
if (v == fat || vis[v]) continue;
get_root(v, u, tot);
siz[u] += siz[v];
maxs[u] = max(siz[v], maxs[u]);
}
maxs[u] = max(maxs[u], tot - siz[u]);
if (!rt || maxs[u] < maxs[rt]) {
rt = u;
}
}
void dfs(int u, int fat, int d, int from) {
can[++ can[0]] = u;
dis[u] = d;
tp[u] = from;
for (auto [v, w] : son[u]) {
if (v == fat || vis[v]) continue;
dfs(v, u, d + w, from);
}
}
void calc(int u) {
can[0] = 0;
can[++ can[0]] = u;
dis[u] = 0;
tp[u] = u;
for (auto [v, w] : son[u]) {
if (vis[v]) continue;
dfs(v, u, w, v);
}
sort(can + 1, can + can[0] + 1, cmp);
for (int i = 1; i <= m; ++ i) {
int l = 1, r = can[0];
if (ok[i]) continue;
while (l < r) {
if (dis[can[l]] + dis[can[r]] > q[i]) {
r --;
}
else if (dis[can[l]] + dis[can[r]] < q[i]) {
++ l;
}
else if (tp[can[l]] == tp[can[r]]) {
if (dis[can[r]] == dis[can[r - 1]]) {
-- r;
}
else ++ l;
}
else {
ok[i] = true;
break;
}
}
}
}
void work(int u) {
vis[u] = true;
calc(u);
for (auto [v, w] : son[u]) {
if (vis[v]) continue;
rt = 0;
get_root(v, 0, siz[v]);
work(rt);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1, x, y, z; i < n; ++ i) {
cin >> x >> y >> z;
son[x].push_back({y, z});
son[y].push_back({x, z});
}
for (int i = 1; i <= m; ++ i) {
cin >> q[i];
if (!q[i]) ok[i] = 1;
}
maxs[0] = n;
get_root(1, 0, n);
work(rt);
for (int i = 1; i <= m; ++ i) {
if (ok[i]) {
cout << "AYE" << '\n';
}
else {
cout << "NAY" << '\n';
}
}
return 0;
}
最后此篇关于「学习笔记」略谈点分治的文章就讲到这里了,如果你想了解更多关于「学习笔记」略谈点分治的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 9 年前。 Improve
介绍篇 什么是MiniApis? MiniApis的特点和优势 MiniApis的应用场景 环境搭建 系统要求 安装MiniApis 配置开发环境 基础概念 MiniApis架构概述
我正在从“JavaScript 圣经”一书中学习 javascript,但我遇到了一些困难。我试图理解这段代码: function checkIt(evt) { evt = (evt) ? e
package com.fastone.www.javademo.stringintern; /** * * String.intern()是一个Native方法, * 它的作用是:如果字
您会推荐哪些资源来学习 AppleScript。我使用具有 Objective-C 背景的传统 C/C++。 我也在寻找有关如何更好地开发和从脚本编辑器获取更快文档的技巧。示例提示是“查找要编写脚本的
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 4年前关闭。 Improve thi
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve thi
关闭。这个问题不符合 Stack Overflow guidelines 。它目前不接受答案。 想改善这个问题吗?更新问题,以便堆栈溢出为 on-topic。 6年前关闭。 Improve this
我是塞内加尔的阿里。我今年60岁(也许这是我真正的问题-笑脸!!!)。 我正在学习Flutter和Dart。今天,我想使用给定数据模型的列表(它的名称是Mortalite,请参见下面的代码)。 我尝试
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题? Update the question所以它是on-topic对于堆栈溢出。 9年前关闭。 Improve this que
学习 Cappuccino 的最佳来源是什么?我从事“传统”网络开发,但我对这个新框架非常感兴趣。请注意,我对 Objective-C 毫无了解。 最佳答案 如上所述,该网站是一个好地方,但还有一些其
我正在学习如何使用 hashMap,有人可以检查我编写的这段代码并告诉我它是否正确吗?这个想法是有一个在公司工作的员工列表,我想从 hashMap 添加和删除员工。 public class Staf
我正在尝试将 jQuery 与 CoffeScript 一起使用。我按照博客中的说明操作,指示使用 $ -> 或 jQuery -> 而不是 .ready() 。我玩了一下代码,但我似乎无法理解我出错
还在学习,还有很多问题,所以这里有一些。我正在进行 javascript -> PHP 转换,并希望确保这些做法是正确的。是$dailyparams->$calories = $calories;一条
我目前正在学习 SQL,以便从我们的 Magento 数据库制作一个简单的 RFM 报告,我目前可以通过导出两个查询并将它们粘贴到 Excel 模板中来完成此操作,我想摆脱 Excel 模板。 我认为
我知道我很可能会因为这个问题而受到抨击,但没有人问,我求助于你。这是否是一个正确的 javascript > php 转换 - 在我开始不良做法之前,我想知道这是否是解决此问题的正确方法。 JavaS
除了 Ruby-Doc 之外,哪些来源最适合获取一些示例和教程,尤其是关于 Ruby 中的 Tk/Tile?我发现自己更正常了 http://www.tutorialspoint.com/ruby/r
我只在第一次收到警告。这正常吗? >>> cv=LassoCV(cv=10).fit(x,y) C:\Python27\lib\site-packages\scikit_learn-0.14.1-py
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be
我是一名优秀的程序员,十分优秀!