题目链接:Race

点分治基本题,从这题简单阐述点分治如何思考问题的。点分治常见的解决一类问题,就是树里面的一些路径类问题。比如一些计数是最常见的。

点分治的一个核心计数思想:

如图所见,对于某个点而言,我们将它作为根,那么它的子树并排地排起来,我们依次遍历每棵树并累计树。

我们容易知道,包括这个点的路径分为两类:

  1. 只会存在于某个子树中

  2. 由两个子树类路径拼接而成

红色为只经过一棵子树的路径,蓝色为两条拼接在一起。很显然的是,我们很容易在遍历的时候维护前缀和路径长以及它的边数量,那么对于同一棵子树里面的路径,我们可以增加一个路径长为 \(0\),边数为 \(0\) 的前缀路径和它前缀路径组合,而对于两棵子树,这里的两棵子树其中一棵为当前子树,另一棵为即为前面那一堆累计的“子树群”,如上图所示,我们遍历完第一棵的子树的信息不删除,继续累计,就是子树群的信息了。当然常见的也可以先算出总的,然后算补集信息即可。而我们可以开一个桶,记录这些路径长的集合,对于每一种路径长,我们记录它的最小边数,然后拼边即可。

点分治使得可以访问到每个点为根的树的信息维护复杂度降低,其次,我们注意到如果和大于 \(k\) 的前缀路径显然无法拼出等于 \(k\) 的路径长,所以可以剪枝掉,我们用数组当桶即可。

剩余细节见代码注释即可

参照代码
#include <bits/stdc++.h>

//#pragma GCC optimize("Ofast,unroll-loops")

#define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope> #endif using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; template <typename T>
int disc(T* a, int n)
{
return unique(a + 1, a + n + 1) - (a + 1);
} template <typename T>
T lowBit(T x)
{
return x & -x;
} template <typename T>
T Rand(T l, T r)
{
static mt19937 Rand(time(nullptr));
uniform_int_distribution<T> dis(l, r);
return dis(Rand);
} template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
return (a % b + b) % b;
} template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
a %= c;
T1 ans = 1;
for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
return modt(ans, c);
} template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')sign = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
x *= sign;
} template <typename T, typename... U>
void read(T& x, U&... y)
{
read(x);
read(y...);
} template <typename T>
void write(T x)
{
if (typeid(x) == typeid(char))return;
if (x < 0)x = -x, putchar('-');
if (x > 9)write(x / 10);
putchar(x % 10 ^ 48);
} template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
write(x), putchar(c);
write(c, y...);
} template <typename T11, typename T22, typename T33>
struct T3
{
T11 one;
T22 tow;
T33 three; bool operator<(const T3 other) const
{
if (one == other.one)
{
if (tow == other.tow)return three < other.three;
return tow < other.tow;
}
return one < other.one;
} T3() { one = tow = three = 0; } T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
{
}
}; template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
if (x < y)x = y;
} template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
if (x > y)x = y;
} constexpr int N = 1e6 + 10; //k为1e6,桶大小不会超过1e6
constexpr int INF = 1e9 + 7;
int treeMin[N];
pii dist[N]; //包括路径和边数
int cnt; //dist的数量
int maxSon, root, sumSize; //最重儿子,当前根,当前树大小
bool del[N]; //该点是否删除
vector<pii> child[N];
int siz[N];
int k, ans = INF;
int edgeMinSize[N]; //开一个桶,对每个长度的路径记录需要的最小边长 //树重心作为根,算出树重心
inline void makeRoot(const int curr, const int fa)
{
siz[curr] = 1;
int maxSize = 0; //最大子树
for (const auto nxt : child[curr] | views::keys)
{
if (nxt == fa or del[nxt])continue;
makeRoot(nxt, curr);
siz[curr] += siz[nxt];
uMax(maxSize, siz[nxt]);
}
uMax(maxSize, sumSize - siz[curr]); //可能是另一半边子树更大
if (maxSize < maxSon)maxSon = maxSize, root = curr; //更新重心
} //统计前缀路径和边的数量
inline void get_dis(const int curr, const int fa, const int sum, const int edgeCnt)
{
if (sum > k)return; //剪枝,题目要求和为k,这条边长不要
dist[++cnt] = pii(sum, edgeCnt);
for (const auto [nxt,d] : child[curr])if (nxt != fa and !del[nxt])get_dis(nxt, curr, sum + d, edgeCnt + 1);
} //统计此子树答案
inline void calc(const int curr)
{
cnt = 0;
edgeMinSize[0] = 0; //可能自身路径就是个符合题意的,加一条长为0,边数为0的路径
for (const auto [nxt,d] : child[curr])
{
if (del[nxt])continue;
int pre = cnt; //方便找到新的子树的dist记录起点
get_dis(nxt, curr, d, 1);
forn(i, pre+1, cnt)uMin(ans, edgeMinSize[k - dist[i].first] + dist[i].second); //两路径凑一边
forn(i, pre+1, cnt)uMin(edgeMinSize[dist[i].first], dist[i].second); //更新对应长度的最小值
}
forn(i, 1, cnt)edgeMinSize[dist[i].first] = INF; //删除这些边
edgeMinSize[0] = INF; //删除该点
} //点分治
inline void div(const int curr)
{
del[curr] = true;
calc(curr);
for (const auto nxt : child[curr] | views::keys)
{
if (del[nxt])continue;
maxSon = sumSize = siz[nxt];
makeRoot(nxt, 0);
div(root);
}
} int n; inline void solve()
{
cin >> n >> k;
forn(i, 1, n-1)
{
int u, v, d;
cin >> u >> v >> d;
++u, ++v; //变为从1开始
child[u].emplace_back(v, d);
child[v].emplace_back(u, d);
}
maxSon = sumSize = n;
fill_n(edgeMinSize, N, INF);
makeRoot(1, 0); //也可以继续考虑重算size减小常数,不过不太需要
div(root);
cout << (ans == INF ? -1 : ans);
} signed int main()
{
Spider
//------------------------------------------------------
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test)solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
}
\[时间复杂度为 \ O(n\log{n})
\]

P4149 [IOI2011] Race 题解的更多相关文章

  1. 模板—点分治B(合并子树)(洛谷P4149 [IOI2011]Race)

    洛谷P4149 [IOI2011]Race 点分治作用(目前只知道这个): 求一棵树上满足条件的节点二元组(u,v)个数,比较典型的是求dis(u,v)(dis表示距离)满足条件的(u,v)个数. 算 ...

  2. 洛谷P4149 [IOI2011]Race(点分治)

    题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KK ,且边的数量最小. 输入输出格式 输入格式:   第一行:两个整数 n,kn,k . 第二至 nn 行:每行三个整数,表示一条无向边的 ...

  3. [洛谷P4149][IOI2011]Race

    题目大意:给一棵树,每条边有边权.求一条简单路径,权值和等于$K$,且边的数量最小. 题解:点分治,考虑到这是最小值,不满足可减性,于是点分中的更新答案的地方计算重复的部分要做更改,就用一个数组记录前 ...

  4. P4149 [IOI2011]Race 点分治

    思路: 点分治 提交:5次 题解: 刚开始用排序+双指针写的,但是调了一晚上,总是有两个点过不了,第二天发现原因是排序时的\(cmp\)函数写错了:如果对于路径长度相同的,我们从小往大按边数排序,当双 ...

  5. P4149 [IOI2011]Race

    对于这道题,明显是点分治,权值等于k,可以用桶统计树上路径(但注意要清空); 对于每颗子树,先与之前的子树拼k,再更新桶,维护t["len"]最小边数; #include < ...

  6. LUOGU P4149 [IOI2011]Race

    题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KKK ,且边的数量最小. 输入输出格式 输入格式: 第一行:两个整数 n,kn,kn,k . 第二至 nnn 行:每行三个整数,表示一条无 ...

  7. 洛谷$P4149\ [IOI2011]\ Race$ 点分治

    正解:点分治 解题报告: 传送门$QwQ$ 昂先不考虑关于那个长度的限制考虑怎么做? 就开个桶,记录所有边的取值,每次加入边的时候查下是否可行就成$QwQ$ 然后现在考虑加入这个长度的限制?就考虑把这 ...

  8. 洛谷 P4149 [IOI2011]Race-树分治(点分治,不容斥版)+读入挂-树上求一条路径,权值和等于 K,且边的数量最小

    P4149 [IOI2011]Race 题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KK,且边的数量最小. 输入格式 第一行包含两个整数 n, Kn,K. 接下来 n - 1n−1 行 ...

  9. 【BZOJ2599】[IOI2011]Race 树的点分治

    [BZOJ2599][IOI2011]Race Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 100000 ...

  10. BZOJ 2599: [IOI2011]Race( 点分治 )

    数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新 ...

随机推荐

  1. 【每日一题】41. 德玛西亚万岁 (状态压缩DP)

    补题链接:Here 经典状压DP问题 坑点,注意多组输入... const int N = 16, mod = 100000000; int f[N][1 << N]; int a[N]; ...

  2. AIO异步通信。BIO同步阻塞式IO, NIO同步非阻塞通信。

    IO 什么是IO? 它是指计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的.单独的程序一般是让系统为它们完 ...

  3. 数字孪生智慧物流之 Web GIS 地图应用

    前言 随着数字经济时代的来临,新一轮全球化进程速度加快,在大数据.人工智能.物联网等高新技术深度融合下,加快催化智慧物流发展,引领物流行业划入全新时代. 从物流运输到货物分拣再到站点配送,图扑软件数据 ...

  4. 最全!即学即会 Serverless Devs 基础入门(下)

    作者 | 刘宇(阿里云 Serverless 产品经理) 在上篇<最全!即学即会 Serverless Devs 基础入门>中,我们阐述了工具链的重要性,并对安装方式 & 密钥配置 ...

  5. 使用 Serverless Devs 插件快速部署前端应用

    作者| 邓超 Serverless Devs 开源贡献者 背景 我们在 上文 [Aliyun] [FC] 如何使用 @serverless-devs/s 部署静态网站到函数计算 中,详细的介绍了如何通 ...

  6. vue学习笔记 七、方法的定义和使用

    系列导航 vue学习笔记 一.环境搭建 vue学习笔记 二.环境搭建+项目创建 vue学习笔记 三.文件和目录结构 vue学习笔记 四.定义组件(组件基本结构) vue学习笔记 五.创建子组件实例 v ...

  7. .NET静态代码织入——肉夹馍(Rougamo)发布2.2

    肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...

  8. K8S_IPV6 POD与数据库联通方法以及快速网络调试的一个思路

    K8S_IPV6 POD与数据库联通方法以及快速网络调试的一个思路 背景 前端时间搭建了一套K8S only IPV6 Single Stack的测试环境 因为自己长时间不搞K8S了, 并且IPV6的 ...

  9. 【转帖】You can now run a GPT-3-level AI model on your laptop, phone, and Raspberry Pi

    https://arstechnica.com/information-technology/2023/03/you-can-now-run-a-gpt-3-level-ai-model-on-you ...

  10. [转帖]Linux系统下cpio命令详解

    简介 cpio主要是解压或者将文件压缩到指定文件中即copy-in和copy-out模式. 参数说明 参数 参数说明 -i copy-in模式,解压文件 -o copy-out模式,即压缩文件 -d ...