[IOI2011]Race
[IOI2011]Race
题面描述
有一棵\(n\)个点 \(n-1\)条边的树 每条边都有一定的权值
求树上两点 使其的简单路径权值和为\(k\)且边数最少
输出为所用边数
具体详见原题
前置知识
点分治
注意事项
敲黑板
俺因为这个挂了有一会
思路
裸的点分治
一般你可以用点分治
但是注意到有边的限制(毕竟是IOI
于是在看到\(k \leq 10^6\)后果断开桶 空间换时间
加上本身自带的减小常数buff 跑得飞快
具体开桶的话就是拿一个\(Edge\)数组记录所用边数
\(Edge[i]\)就是第i个更新到的所用的边数 跟dis组成一对连体婴儿
在记\(min[i]\)表示长度为i最少要用多长的边
那么我们可以得到式子
\(ans = \min_{i=1}^{tot}min[k - dis[i]] + Edge[i]\)
\(min[i] = \min_{i=1}^{tot}Edge[i]\)
然后每次做完记得赋初值 即\(\infty\)
Tips
别像我一样喜欢压行结果调了2天不到一点
错误示范
for (auto v : G[u]) if(!delete[son = v.first]) {
...
}
还有的就是因为 \(dis\) > \(k\) 直接return
就行了
这样的只用判一下ans是否 >n就好了
记得赋初值
int ans = MAX_INT;
Code
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
int read(int x = 0, bool f = false, char ch = getchar()) {
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return f ? ~x + 1 : x;
}
const int N = 2e5 + 5, M = 1e6 + 5;
int n, k, root, son, ans = INT_MAX, tot, SIZE, u, v, w;
int mx[N], siz[N], anc[N], mn[M], dis[N], Edge[N];
bool del[N];
vector<PII> G[N];
#define x first
#define y second
void find(int u, int fa) {
siz[u] = 1; mx[u] = 0;
for (auto v : G[u]) {
int son = v.x;
if (del[son] || son == fa) continue;
find(son, u); siz[u] += siz[son];
mx[u] = max(mx[u], siz[son]);
}
mx[u] = max(mx[u], SIZE - siz[u]);
if (mx[u] < mx[root]) root = u;
}
void dfs(int u, int fa, int p, int dep) {
if (p > k) return;
dis[++ tot] = p; Edge[tot] = dep;
for (auto v : G[u]) {
int son = v.x;
if (del[son = v.x] || son == fa) continue;
dfs(son, u, p + v.y, dep + 1);
}
}
void calc(int u) {
mn[0] = tot = 0;
for (auto v : G[u]) {
int son = v.x;
if (del[son]) continue;
int cnt = tot;
dfs(son, u, v.y, 1);
for (int i = cnt + 1; i <= tot; ++i)
ans = min(ans, mn[k - dis[i]] + Edge[i]);
for (int i = cnt + 1; i <= tot; ++i)
mn[dis[i]] = min(mn[dis[i]], Edge[i]);
}
for (int i = 1; i <= tot; ++i) mn[dis[i]] = 1e9;
}
void solve(int u) {
del[u] = true; calc(u);
for (auto v : G[u]) {
int son = v.x;
if (del[son]) continue;
root = 0; SIZE = siz[son]; find(son, u); solve(root);
}
}
signed main(int argc, char *argv[]) {
n = read(), k = read();
for (int i = 1; i <= n - 1; ++i)
u = read() + 1, v = read() + 1, w = read(),
G[u].push_back(make_pair(v,w)),
G[v].push_back(make_pair(u,w));
mx[root = 0] = (SIZE = n) + 1;
memset(mn, 0x3f, sizeof mn);
SIZE = n, find(1, 0); solve(root);
return printf("%d\n", ans >= n ? -1 : ans), 0;
}
本文来自博客园,作者:xxcxu,转载请注明原文链接:https://www.cnblogs.com/Maraschino/p/15172435.html