P4149 [IOI2011] Race 题解
题目链接:Race
点分治基本题,从这题简单阐述点分治如何思考问题的。点分治常见的解决一类问题,就是树里面的一些路径类问题。比如一些计数是最常见的。
点分治的一个核心计数思想:
如图所见,对于某个点而言,我们将它作为根,那么它的子树并排地排起来,我们依次遍历每棵树并累计树。
我们容易知道,包括这个点的路径分为两类:
只会存在于某个子树中
由两个子树类路径拼接而成
红色为只经过一棵子树的路径,蓝色为两条拼接在一起。很显然的是,我们很容易在遍历的时候维护前缀和路径长以及它的边数量,那么对于同一棵子树里面的路径,我们可以增加一个路径长为 \(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();
}
\]
P4149 [IOI2011] Race 题解的更多相关文章
- 模板—点分治B(合并子树)(洛谷P4149 [IOI2011]Race)
洛谷P4149 [IOI2011]Race 点分治作用(目前只知道这个): 求一棵树上满足条件的节点二元组(u,v)个数,比较典型的是求dis(u,v)(dis表示距离)满足条件的(u,v)个数. 算 ...
- 洛谷P4149 [IOI2011]Race(点分治)
题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KK ,且边的数量最小. 输入输出格式 输入格式: 第一行:两个整数 n,kn,k . 第二至 nn 行:每行三个整数,表示一条无向边的 ...
- [洛谷P4149][IOI2011]Race
题目大意:给一棵树,每条边有边权.求一条简单路径,权值和等于$K$,且边的数量最小. 题解:点分治,考虑到这是最小值,不满足可减性,于是点分中的更新答案的地方计算重复的部分要做更改,就用一个数组记录前 ...
- P4149 [IOI2011]Race 点分治
思路: 点分治 提交:5次 题解: 刚开始用排序+双指针写的,但是调了一晚上,总是有两个点过不了,第二天发现原因是排序时的\(cmp\)函数写错了:如果对于路径长度相同的,我们从小往大按边数排序,当双 ...
- P4149 [IOI2011]Race
对于这道题,明显是点分治,权值等于k,可以用桶统计树上路径(但注意要清空); 对于每颗子树,先与之前的子树拼k,再更新桶,维护t["len"]最小边数; #include < ...
- LUOGU P4149 [IOI2011]Race
题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KKK ,且边的数量最小. 输入输出格式 输入格式: 第一行:两个整数 n,kn,kn,k . 第二至 nnn 行:每行三个整数,表示一条无 ...
- 洛谷$P4149\ [IOI2011]\ Race$ 点分治
正解:点分治 解题报告: 传送门$QwQ$ 昂先不考虑关于那个长度的限制考虑怎么做? 就开个桶,记录所有边的取值,每次加入边的时候查下是否可行就成$QwQ$ 然后现在考虑加入这个长度的限制?就考虑把这 ...
- 洛谷 P4149 [IOI2011]Race-树分治(点分治,不容斥版)+读入挂-树上求一条路径,权值和等于 K,且边的数量最小
P4149 [IOI2011]Race 题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KK,且边的数量最小. 输入格式 第一行包含两个整数 n, Kn,K. 接下来 n - 1n−1 行 ...
- 【BZOJ2599】[IOI2011]Race 树的点分治
[BZOJ2599][IOI2011]Race Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 100000 ...
- BZOJ 2599: [IOI2011]Race( 点分治 )
数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新 ...
随机推荐
- Codeforces Round #544 (Div. 3)简单题解
复健,时间有限题解比较简陋 A. Middle of the Contest 将小时转成分钟,得到起止时间在一天中的分钟数,取平均值即可,复杂度O(1).平均值转换会时间的时候注意前导0. void ...
- Kotlin 协程真的比 Java 线程更高效吗?
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/-OcCDI4L5GR8vVXSYhXJ7w作者:吴越 网上几乎全部介绍Kotlin的文章都会说 ...
- debian更新openssh 9.6
先更新一下,然后安装libssl-dev zlib1g-dev依赖文件 apt update apt install build-essential apt-get install -y libssl ...
- 13、SpringBoot-配置文件里密码加密
系列导航 springBoot项目打jar包 1.springboot工程新建(单模块) 2.springboot创建多模块工程 3.springboot连接数据库 4.SpringBoot连接数据库 ...
- webservice(AXIS)客户端生成方法
如何根据apache的axis生成的WebServices服务接口生成客户端代码一.下载axis-bin-1_4.zip 官网下载地址: https://mirrors.bfsu.edu. ...
- webpack配置自动打包相关的参数
- 解决xshell-ssh远程登录,只能通过public key登录导致无法登录的情况
xshell无法通过密码登录的问题如下: 1.登录主机:vi /etc/ssh/sshd_config 2.搜索关键字:PasswordAuthentication 3.将PasswordAuthen ...
- java基础-常用类-day11
目录 1. 包装类 2. Integer类 3.util.date 4. java.sql.Date 5. SimpleDateFormat 6. Calendar 7. Math 8. String ...
- JMS微服务远程调用性能测试 vs .Net Core gRPC服务
gRPC性能测试(.net 5) 创建一个最简单的gRPC服务,服务器代码如下: using Grpc.Core; using Microsoft.Extensions.Logging; using ...
- [转帖]mysql 里 CST 时区的坑
mysql 里 CST 时区的坑 一.问题简述 mysql 里 CST 时区是个非常坑的概念,因为在 mysql 里CST既表示中国也表示美国的时区.但是在Jdk代码里,CST 这个字符串被理解为Ce ...