[IOI2011]Race
题意
思考
简要题意:给一棵树,每条边有权。求一条简单路径,权值和等于 K,且边的数量最小。
由于这条最小路径可以是所有路径中的任意一个,所以所有等于 K 的路径我们必须考虑到,关于树上的路径统计问题,我们选用点分治。
这样一想就是点分治裸题了,由于 K≤1e6,我们可以开个桶然后套路计算了,对于每颗子树计算完后再加入答案,避免重复计算(大于 K 的路径也可以剪点枝)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 200020;
const int M = 1000010;
const int oo = 0x3f3f3f3f;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x * f;
}
struct node{
int nxt, to, dis;
}edge[N << 1];
int head[N], num;
void build(int from, int to, int dis){
edge[++num].nxt = head[from];
edge[num].to = to;
edge[num].dis = dis;
head[from] = num;
}
int root, ANS, n, k, sum, vis[N], f[N], sz[N], dist[N], ans[M];
void getroot(int u, int fa){
sz[u] = 1; f[u] = 0;
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa || vis[v]) continue;
getroot(v, u);
sz[u] += sz[v];
f[u] = max(f[u], sz[v]);
}
f[u] = max(f[u], sum - sz[u]);
if(f[root] > f[u]) root = u;
}
void dfs(int u, int fa, int cnt){
if(dist[u] > k) return;
ANS = min(ANS, ans[ k - dist[u] ] + cnt);
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa || vis[v]) continue;
dist[v] = dist[u] + edge[i].dis;
dfs(v, u, cnt + 1);
}
}
void add(int u, int fa, int cnt){
if(dist[u] > k) return;
ans[ dist[u] ] = min(ans[ dist[u] ], cnt);
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa || vis[v]) continue;
add(v, u, cnt + 1);
}
}
void clearx(int u, int fa, int dist){
if(dist > k) return;
ans[ dist ] = oo;
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa || vis[v]) continue;
clearx(v, u, edge[i].dis + dist);
}
}
void solve(int u){
vis[u] = 1; ans[0] = 0;
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(vis[v]) continue;
dist[v] = edge[i].dis;
dfs(v, u, 1);
add(v, u, 1);
}
clearx(u, 0, 0);
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(vis[v]) continue;
root = 0;
sum = sz[v];
getroot(v, u);
solve(root);
}
}
int main(){
f[0] = ANS = oo;
memset(ans, 0x3f, sizeof(ans));
n = read(), k = read();
for(int i=1; i<=n-1; i++){
int u, v, d;
u = read(), v = read(), d = read();
build(u + 1, v + 1, d);
build(v + 1, u + 1, d);
}
sum = n;
root = 0;
getroot(1, 0);
solve(root);
if(ANS != oo) cout << ANS;
else puts("-1");
return 0;
}
总结
写完之后 T 了 n 回,发现是自己找重心的时候 f[] 数组没清零,导致这个复杂度啊,有点点大。以后要记得重置数组,不然都没法查错啊这个。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 用一种新的分类方法梳理设计模式的脉络