[BZOJ2599][IOI2011]Race

[BZOJ2599][IOI2011]Race

试题描述

给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000

输入

第一行 两个整数 n, k
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)

输出

一个整数 表示最小边数量 如果不存在这样的路径 输出-1

输入示例

4 3
0 1 1
1 2 2
1 3 4

输出示例

2

数据规模及约定

见“试题描述

题解

点分治裸题。我还调了半天TAT。。。好久没写什么都忘了。。。

每次找子树的中心往下递归处理,合并的时候开一个大小为 k 的数组 Cnt[i] 记录边权和为 i 时所需的最少边的条数,转移也很简单。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 200010
#define maxm 400010
#define maxk 1000010
#define oo 2147483647
int n, m, k, head[maxn], next[maxm], to[maxm], dist[maxm];

void AddEdge(int a, int b, int c) {
	to[++m] = b; dist[m] = c; next[m] = head[a]; head[a] = m;
	swap(a, b);
	to[++m] = b; dist[m] = c; next[m] = head[a]; head[a] = m;
	return ;
}

bool vis[maxn];
int root, Siz, siz[maxn], f[maxn];
void getroot(int u, int fa) {
	siz[u] = 1; f[u] = 0;
	for(int e = head[u]; e; e = next[e]) if(to[e] != fa && !vis[to[e]]) {
		getroot(to[e], u);
		siz[u] += siz[to[e]];
		f[u] = max(f[u], siz[to[e]]);
	}
	f[u] = max(f[u], Siz - siz[u]);
	if(f[u] < f[root]) root = u;
	return ;
}
int ToT, d[maxn], dep[maxn], Cnt[maxk], mark[maxk], ans;
void dfs(int u, int fa, int dis, int depth) {
	d[++ToT] = dis; dep[ToT] = depth;
	for(int e = head[u]; e; e = next[e]) if(!vis[to[e]] && to[e] != fa && dis + dist[e] <= k)
		dfs(to[e], u, dis + dist[e], depth + 1);
	return ;
}
void solve(int u) {
//	printf("%d\n", u);
	vis[u] = 1;
	bool flag = 0;
	for(int e = head[u]; e; e = next[e]) if(!vis[to[e]]) {
		ToT = 0; dfs(to[e], u, dist[e], 1);
//		printf("%d: ", to[e]); for(int i = 1; i <= ToT; i++) printf("%d ", d[i]); putchar('\n');
		for(int i = 1; i <= ToT; i++) if(d[i] == k) ans = min(ans, dep[i]);
		if(flag) {
			for(int i = 1; i <= ToT; i++) if(d[i] < k && mark[k-d[i]] == u)
				ans = min(ans, dep[i] + Cnt[k-d[i]]);
		}
		for(int i = 1; i <= ToT; i++) if(d[i] < k) {
			if(mark[d[i]] != u) Cnt[d[i]] = dep[i], mark[d[i]] = u;
			else Cnt[d[i]] = min(Cnt[d[i]], dep[i]);
		}
		flag = 1;
	}
	for(int e = head[u]; e; e = next[e]) if(!vis[to[e]]) {
		f[0] = n + 1; Siz = siz[to[e]]; root = 0; getroot(to[e], u);
		solve(root);
	}
	return ;
}

int main() {
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	n = read(); k = read();
	for(int i = 1; i < n; i++) {
		int a = read() + 1, b = read() + 1, c = read();
		AddEdge(a, b, c);
	}
	
	f[0] = n + 1; Siz = n; root = 0; getroot(1, 0);
	ans = oo;
	solve(root);
	
	if(ans < oo) printf("%d\n", ans);
	else puts("-1");
	
	return 0;
}

IOI 居然有这么水的题!难道是因为年份太早了?!

posted @ 2016-08-07 10:58  xjr01  阅读(252)  评论(0编辑  收藏  举报