zoukankan      html  css  js  c++  java
  • [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
    ", 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('
    ');
    		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
    ", ans);
    	else puts("-1");
    	
    	return 0;
    }
    

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

  • 相关阅读:
    面试经验
    java常见面试题目(三)
    java常见面试题目(二)
    java常见面试题目(一)
    Java编程思想:第2章 一切都是对象
    汇编语言:第九章 转移指令的原理
    汇编语言: 实验七 寻址方式在结构化数据访问中的应用
    汇编语言:第八章 数据处理的两个基本问题
    汇编语言:实验六 实践课程中的程序
    汇编语言:第七章 更灵活定位内存地址的方法
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5745718.html
Copyright © 2011-2022 走看看