洛谷传送门
BZOJ传送门
题目描述
给一棵树,每条边有权。求一条简单路径,权值和等于 K K ,且边的数量最小。
输入输出格式
输入格式:
第一行:两个整数 。
第二至 n n 行:每行三个整数,表示一条无向边的两端和权值 (注意点的编号从 开始)。
输出格式:
一个整数,表示最小边数量。
如果不存在这样的路径,输出 −1 − 1 。
输入输出样例
输入样例#1:
4 3
0 1 1
1 2 2
1 3 4
输出样例#1:
2
说明
n≤200000,K≤1000000 n ≤ 200000 , K ≤ 1000000 。
解题分析
显然是一道点分治的裸题…因为 K≤1000000 K ≤ 1000000 ,所以开个数组记录同层最优解即可。
注意距离为0的最优解为0。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cctype>
#include <limits.h>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 200500
#define INF 100000000
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int dot, kth, ans = INF, cnt, root, deal;
int head[MX], rec[1000050], siz[MX], fat[MX],
mx[MX], buf[MX], dep[MX], dis[MX];
bool vis[MX];
struct Edge
{
int to, nex, len;
}edge[MX << 1];
IN void addedge(const int &from, const int &to, const int &len)
{
edge[++cnt] = {to, head[from], len};
head[from] = cnt;
}
namespace Dtdv
{
void DFS(const int &now, const int &fa)
{
siz[now] = 1;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fa || vis[edge[i].to]) continue;
DFS(edge[i].to, now);
siz[now] += siz[edge[i].to];
}
}
void getroot(const int &now, const int &fa)
{
mx[now] = 0;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fa || vis[edge[i].to]) continue;
getroot(edge[i].to, now);
mx[now] = std::max(mx[now], siz[edge[i].to]);
}
mx[now] = std::max(deal - siz[now], mx[now]);
if(mx[now] < mx[root]) root = now;
}
void reset(const int &now, const int &fa, const bool &typ)//清零或更新
{
if(dis[now] <= kth)
{
if(typ) rec[dis[now]] = std::min(rec[dis[now]], dep[now]);
else rec[dis[now]] = INF;
}
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fa || vis[edge[i].to]) continue;
reset(edge[i].to, now, typ);
}
}
void calc(const int &now, const int &fa)//计算答案
{
if(dis[now] <= kth) ans = std::min(ans, rec[kth - dis[now]] + dep[now]);
for (R int i = head[now]; i; i = edge[i].nex)
{
if(vis[edge[i].to] || edge[i].to == fa) continue;
dep[edge[i].to] = dep[now] + 1;
dis[edge[i].to] = dis[now] + edge[i].len;
calc(edge[i].to, now);
}
}
void solve(R int now, const int &fa)
{
vis[now] = true; rec[0] = 0;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(vis[edge[i].to]) continue;
dis[edge[i].to] = edge[i].len; dep[edge[i].to] = 1;
calc(edge[i].to, now);
reset(edge[i].to, now, true);
}
for (R int i = head[now]; i; i = edge[i].nex)
{
if(vis[edge[i].to]) continue;
reset(edge[i].to, now, false);//注意到一层大小远小于1000000,不用memset。
}
for (R int i = head[now]; i; i = edge[i].nex)
{
if(vis[edge[i].to]) continue;
DFS(edge[i].to, now);
deal = siz[edge[i].to]; root = 0;
getroot(edge[i].to, now);
solve(root, now);
}
}
}
int main(void)
{
int a, b, c;
in(dot); in(kth); deal = mx[root = 0] = dot; memset(rec, 63, sizeof(rec));
for (R int i = 1; i < dot; ++i)
in(a), in(b), in(c), addedge(a + 1, b + 1, c), addedge(b + 1, a + 1, c);
Dtdv::DFS(1, 0); Dtdv::getroot(1, 0); Dtdv::solve(root, 0);
if(ans > 1000000) printf("-1");
else printf("%d", ans);
}