[BZOJ 2599][IOI2011]Race
由于一些琐事,很长时间没有更新博客了……尽管做了不少题,但还是给我些时间整理思绪吧……
大方向肯定是点分治没错啦……
点分治的题处理经过根的路径通常有两种套路……一种是类似BZOJ 1468那样,收集所有儿子的信息,然后一次性合并,并排除不合法的情况。另一种类似于这道题,顺次处理儿子的信息,处理一个合并一个。
具体的思路是,用[tex]p[x][/tex]表示目前已知的所有从根开始长度为[tex]x[/tex]的路径(当然根本身也算上啦,所以说一开始就把[tex]p[root][/tex]设为0),然后根搜集信息时每次对一个儿子进行DFS,假设某结点[tex]x[/tex]到根的距离为[tex]d[x][/tex],那么显然[tex]x[/tex]对答案的贡献为[tex]p[k-d[x]][/tex],之后把这个子树合并到[tex]p[/tex]中。
注意清理[tex]p[/tex]的时候不能直接暴力memset……这样会被卡常致死。更好的方法是对于每次把子树合并到[tex]p[/tex]中时,把[tex]x[/tex]记录下来,然后清理[tex]p[/tex]时常数就小很多了。
代码:
/**************************************************************
Problem: 2599
User: danihao123
Language: C++
Result: Accepted
Time:14640 ms
Memory:23532 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
const int maxn = 200005;
const int maxm = maxn * 2;
const int maxk = 1000005;
struct Edge {
Edge *next;
int to, dist;
};
Edge pool[maxm];
int graph_cnt;
Edge *first[maxn];
inline void clear_graph() {
graph_cnt = 0;
memset(first, 0, sizeof first);
}
inline void add_edge(int u, int v, int d) {
Edge *e = &pool[graph_cnt++];
e->next = first[u];
first[u] = e;
e->to = v;
e->dist = d;
}
int k;
bool vis[maxn];
int siz[maxn];
void gen_siz(int x, int fa) {
siz[x] = 1;
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v] && v != fa) {
gen_siz(v, x);
siz[x] += siz[v];
}
}
}
int now_root, best_root;
void gen_best_root(int x, int fa) {
bool OK = siz[x]*2 >= siz[now_root];
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v] && v != fa) {
gen_best_root(v, x);
OK = OK && (siz[v]*2 <= siz[now_root]);
}
}
if(OK) {
best_root = x;
}
}
int buf[maxk];
int ans;
void deal_ans(int x, int fa, int dep, int d) {
if(d > k) {
return;
}
ans = min(ans, dep + buf[k-d]);
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v] && v != fa) {
deal_ans(v, x, dep + 1, d + e->dist);
}
}
}
void add_to_buf(int x, int fa, int dep, int d) {
if(d > k) {
return;
}
buf[d] = min(buf[d], dep);
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v] && v != fa) {
add_to_buf(v, x, dep + 1, d + e->dist);
}
}
}
void clear_buf(int x, int fa, int dep, int d) {
if(d > k) {
return;
}
buf[d] = 0x3f3f3f3f;
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v] && v != fa) {
clear_buf(v, x, dep + 1, d + e->dist);
}
}
}
void divide(int x) {
gen_siz(x, 0);
now_root = best_root = x; gen_best_root(x, 0);
x = best_root;
vis[x] = true;
buf[0] = 0;
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v]) {
deal_ans(v, x, 1, e->dist);
add_to_buf(v, x, 1, e->dist);
}
}
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v]) {
clear_buf(v, x, 1, e->dist);
}
}
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v]) {
divide(v);
}
}
}
inline int readint() {
int x = 0;
char c = getchar();
while(!isdigit(c)) {
c = getchar();
}
while(isdigit(c)) {
x = x * 10 + (c - '0');
c = getchar();
}
return x;
}
int main() {
int n;
n = readint();
k = readint();
clear_graph();
for(int i = 1; i <= (n-1); i++) {
int u, v, d;
u = readint();
v = readint();
d = readint();
u++; v++;
add_edge(u, v, d);
add_edge(v, u, d);
}
ans = 0x3f3f3f3f;
memset(buf, 0x3f, sizeof buf);
divide(1);
if(ans == 0x3f3f3f3f) {
ans = -1;
}
printf("%d\n", ans);
return 0;
}