【Luogu P4149】[IOI2011]Race(点分治)
自闭了几天后的我终于开始做题了。。然后调了3h一道点分治板子题,调了一天一道IOI。。。
最后还是自己手造数据debug出来的。。。
这题一看:树上路径问题,已知路径长度求balabala,显然是点分治(其实只要有一点点对点分治思想及应用的理解就能知道)。照普通点分治的做法,找重心分治,每次统计子树所有点到根的距离,然后开个桶判断一下是否出现即可(本题还要存一下边数)。然后我们要做的只有暴力统计取min了,时间复杂度\(O(n\log n)\)。
于是本蒟蒻自信满满地打下了以下代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 202000
#define maxk 10001000
struct edge {
int w, to, next;
} e[maxn << 1];
int head[maxn], dis[maxn], save[maxn], save2[maxn], cnt[maxn], vis[maxn], size[maxn], maxp[maxn], ecnt, sum, depth[maxn];
int get[maxk], get2[maxk];
int n, m, k, ans = 0x3f3f3f3f;
#define isdigit(x) ((x) >= '0' && (x) <= '9')
inline int read() {
int res = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) res = (res << 1) + (res << 3) + (c ^ 48), c = getchar();
return res;
}
void adde(int u, int v, int w) {
e[++ecnt] = (edge) {w, v, head[u]};
head[u] = ecnt;
}
int getrt(int x, int fa) {
int rt = 0;
maxp[x] = 0;
size[x] = 1;
for (int i = head[x]; i; i = e[i].next) {
int to = e[i].to;
if (vis[to] || to == fa) continue;
rt = getrt(to, x);
size[x] += size[to];
maxp[x] = max(maxp[x], size[to]);
}
maxp[x] = max(maxp[x], sum - size[x]);
if (maxp[rt] > maxp[x]) rt = x;
return rt;
}
void getdis(int x, int fa) {
save[++save[0]] = dis[x];
cnt[++cnt[0]] = depth[x];
for (int i = head[x]; i; i = e[i].next) {
int to = e[i].to;
if (vis[to] || to == fa) continue;
dis[to] = dis[x] + e[i].w;
depth[to] = depth[x] + 1;
getdis(to, x);
}
}
void work(int x) {
save2[0] = 0;
for (int i = head[x]; i; i = e[i].next) {
int to = e[i].to;
if (vis[to]) continue;
save[0] = cnt[0] = 0;
dis[to] = e[i].w;
depth[to] = 1;
getdis(to, x);
for (int j = 1; j <= save[0]; ++j) {
// get2[save[j]] = min(get2[save[j]], cnt[j]);
if (k >= save[j] && get[k - save[j]])
ans = min(ans, get2[k - save[j]] + cnt[j]);
}
for (int j = 1; j <= save[0]; ++j) {
get[save[j]] = 1, save2[++save2[0]] = save[j];
get2[save[j]] = min(get2[save[j]], cnt[j]);
}
}
for (int i = 1; i <= save2[0]; ++i) get[save2[i]] = 0, get2[save2[i]] = 0x3f3f3f3f;
}
void dfs(int x) {
vis[x] = get[0] = 1;
get2[0] = 0;
work(x);
for (int i = head[x]; i; i = e[i].next) {
int to = e[i].to;
if (vis[to]) continue;
maxp[0] = n;
sum = size[to];
dfs(getrt(to, 0));
}
}
int main() {
memset(get2, 0x3f, sizeof(get2));
n = read(), k = read();
for (int i = 1; i < n; ++i) {
int u = read(), v = read(), w = read();
adde(u + 1, v + 1, w);
adde(v + 1, u + 1, w);
}
maxp[0] = sum = n;
dfs(getrt(1, 0));
if (ans == 0x3f3f3f3f) puts("-1");
else printf("%d\n", ans);
return 0;
}
RE85。爆栈?算了就算爆栈现在除了重写一遍也没有别的解决办法了。。
尝试把两个桶开大2倍,变成90了。
再开大2倍,变成MLE了。看来是数据太大,桶开不下。于是写了个unordered_map,常数极大,TLE到只有20分。
回过头来,发现save里存的路径长度只有\(≤k\)时才对答案有贡献,不符合条件的都可以不存进桶里,桶可以只开到1e6多一点(保证k不越界就行)。加一句特判即可AC。
code:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 200007
#define maxk 1000007
struct edge {
int w, to, next;
} e[maxn << 1];
int head[maxn], dis[maxn], save[maxn], save2[maxn], cnt[maxn], vis[maxn], size[maxn], maxp[maxn], ecnt, sum, depth[maxn];
int get[maxk], get2[maxk];
int n, m, k, ans = 0x3f3f3f3f;
#define isdigit(x) ((x) >= '0' && (x) <= '9')
inline int read() {
int res = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) res = (res << 1) + (res << 3) + (c ^ 48), c = getchar();
return res;
}
void adde(int u, int v, int w) {
e[++ecnt] = (edge) {w, v, head[u]};
head[u] = ecnt;
}
int getrt(int x, int fa) {
int rt = 0;
maxp[x] = 0;
size[x] = 1;
for (int i = head[x]; i; i = e[i].next) {
int to = e[i].to;
if (vis[to] || to == fa) continue;
rt = getrt(to, x);
size[x] += size[to];
maxp[x] = max(maxp[x], size[to]);
}
maxp[x] = max(maxp[x], sum - size[x]);
if (maxp[rt] > maxp[x]) rt = x;
return rt;
}
void getdis(int x, int fa) {
save[++save[0]] = dis[x];
cnt[++cnt[0]] = depth[x];
for (int i = head[x]; i; i = e[i].next) {
int to = e[i].to;
if (vis[to] || to == fa) continue;
dis[to] = dis[x] + e[i].w;
depth[to] = depth[x] + 1;
getdis(to, x);
}
}
void work(int x) {
save2[0] = 0;
for (int i = head[x]; i; i = e[i].next) {
int to = e[i].to;
if (vis[to]) continue;
save[0] = cnt[0] = 0;
dis[to] = e[i].w;
depth[to] = 1;
getdis(to, x);
for (int j = 1; j <= save[0]; ++j) {
// get2[save[j]] = min(get2[save[j]], cnt[j]);
if (k >= save[j] && get[k - save[j]])
ans = min(ans, get2[k - save[j]] + cnt[j]);
}
for (int j = 1; j <= save[0]; ++j)
if (save[j] <= k) {
get[save[j]] = 1, save2[++save2[0]] = save[j];
get2[save[j]] = min(get2[save[j]], cnt[j]);
}
}
for (int i = 1; i <= save2[0]; ++i) get[save2[i]] = 0, get2[save2[i]] = 0x3f3f3f3f;
}
void dfs(int x) {
vis[x] = get[0] = 1;
get2[0] = 0;
work(x);
for (int i = head[x]; i; i = e[i].next) {
int to = e[i].to;
if (vis[to]) continue;
maxp[0] = n;
sum = size[to];
dfs(getrt(to, 0));
}
}
int main() {
memset(get2, 0x3f, sizeof(get2));
n = read(), k = read();
for (int i = 1; i < n; ++i) {
int u = read(), v = read(), w = read();
adde(u + 1, v + 1, w);
adde(v + 1, u + 1, w);
}
maxp[0] = sum = n;
dfs(getrt(1, 0));
if (ans == 0x3f3f3f3f) puts("-1");
else printf("%d\n", ans);
return 0;
}
【Luogu P4149】[IOI2011]Race(点分治)的更多相关文章
- P4149 [IOI2011]Race 点分治
思路: 点分治 提交:5次 题解: 刚开始用排序+双指针写的,但是调了一晚上,总是有两个点过不了,第二天发现原因是排序时的\(cmp\)函数写错了:如果对于路径长度相同的,我们从小往大按边数排序,当双 ...
- LUOGU P4149 [IOI2011]Race
题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KKK ,且边的数量最小. 输入输出格式 输入格式: 第一行:两个整数 n,kn,kn,k . 第二至 nnn 行:每行三个整数,表示一条无 ...
- 洛谷$P4149\ [IOI2011]\ Race$ 点分治
正解:点分治 解题报告: 传送门$QwQ$ 昂先不考虑关于那个长度的限制考虑怎么做? 就开个桶,记录所有边的取值,每次加入边的时候查下是否可行就成$QwQ$ 然后现在考虑加入这个长度的限制?就考虑把这 ...
- 模板—点分治B(合并子树)(洛谷P4149 [IOI2011]Race)
洛谷P4149 [IOI2011]Race 点分治作用(目前只知道这个): 求一棵树上满足条件的节点二元组(u,v)个数,比较典型的是求dis(u,v)(dis表示距离)满足条件的(u,v)个数. 算 ...
- BZOJ 2599: [IOI2011]Race( 点分治 )
数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新 ...
- [IOI2011]Race 点分治
[IOI2011]Race LG传送门 点分治板子题. 直接点分治统计,统计的时候开个桶维护下就好了. 注(tiao)意(le)细(hen)节(jiu). #include<cstdio> ...
- [bzoj2599][IOI2011]Race——点分治
Brief Description 给定一棵带权树,你需要找到一个点对,他们之间的距离为k,且路径中间的边的个数最少. Algorithm Analyse 我们考虑点分治. 对于子树,我们递归处理,所 ...
- 洛谷 4149 [IOI2011]Race——点分治
题目:https://www.luogu.org/problemnew/show/P4149 第一道点分治! 点分治大约是每次找重心,以重心为根做一遍树形dp:然后对于该根的每个孩子,递归下去.递归之 ...
- 洛谷P4149 [IOI2011]Race(点分治)
题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KK ,且边的数量最小. 输入输出格式 输入格式: 第一行:两个整数 n,kn,k . 第二至 nn 行:每行三个整数,表示一条无向边的 ...
- P4149 [IOI2011]Race
对于这道题,明显是点分治,权值等于k,可以用桶统计树上路径(但注意要清空); 对于每颗子树,先与之前的子树拼k,再更新桶,维护t["len"]最小边数; #include < ...
随机推荐
- 2017-2018-2 20155224『网络对抗技术』Exp7:网络欺诈防范
基础问题回答 问:通常在什么场景下容易受到DNS spoof攻击? 同一局域网下,以及各种公共网络. 问:在日常生活工作中如何防范以上两攻击方法? 答:DNS欺骗攻击是很难防御的,因为这种攻击大多数本 ...
- 2017-2018-2 《网络对抗技术》20155322 Exp9 web安全基础
[-= 博客目录 =-] 1-实践目标 1.1-实践介绍 1.2-实践内容 1.3-实践要求 2-实践过程 2.1-HTML 2.2-Injection Flaws 2.3-XSS 2.4-CSRF ...
- maven中添加jetty运行插件
maven项目,用jetty插件运行,对热部署的支持比较好.maven的pom文件加入下面代码 <plugin> <groupId>org.mortbay.je ...
- Android开发——高斯模糊效果的简单实现
0. 前言 在Android开发中,经常在音乐软件中看到高斯模糊效果. 在找遍了所有高斯模糊的算法代码后,发现stackblur的Java实现是最快的.效果如下所示. 1. 高斯模糊效果实现 Bit ...
- python 回溯法 子集树模板 系列 —— 16、爬楼梯
问题 某楼梯有n层台阶,每步只能走1级台阶,或2级台阶.从下向上爬楼梯,有多少种爬法? 分析 这个问题之前用分治法解决过.但是,这里我要用回溯法子集树模板解决它. 祭出元素-状态空间分析大法:每一步是 ...
- Package 设计3:数据源的提取和使用暂存
SSIS 设计系列: Package设计1:选择数据类型.暂存数据和并发 Package设计2:增量更新 Package 设计3:数据源的提取和使用暂存 在使用SSIS Package处理海量数据时, ...
- JavaScript快速入门-ECMAScript基础语法
一.JavaScript引入方式 1.行内式 <script> alert(123); </script> 2.外链式 <script src='custom.js'&g ...
- k8s-rabbitmq-(一)集群部署
K8S版本:1.10.1 rabbitmq版本:3.6.14 从来没用过这个软件,所以对里面很多术语看不太懂.最后通过https://www.kubernetes.org.cn/2629.html 大 ...
- 强化学习算法DQN
1 DQN的引入 由于q_learning算法是一直更新一张q_table,在场景复杂的情况下,q_table就会大到内存处理的极限,而且在当时深度学习的火热,有人就会想到能不能将从深度学习中借鉴方法 ...
- 开源一个最近写的spring与mongodb结合的demo(spring-mongodb-demo)
由于工作需要,给同事们分享了一下mongodb的使用,其中主要就是做了一个spring-data+mongodb的小例子,本着分享的精神,就上传到了github.com上,有需要的同学请移步githu ...