• Luogu5021 [NOIP2018]赛道修建


    题目蓝链

    Description

    给你一棵树,树上每条边都有一个边权。你要在上面选出(m)条没有重复边的路径,使得选出的最短路径尽量的长

    Solution

    最短的最长,这显然就是二分答案

    然后就直接在树上贪心就可以了,对于每一个点把它的字树尽可能多的两两匹配,最后如果有匹配不了的就与当前点连向父亲的边连起来,这个匹配可以用(set)去实现

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define fst first
    #define snd second
    #define squ(x) ((LL)(x) * (x))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    typedef long long LL;
    typedef pair<int, int> pii;
    
    inline int read() {
    	int sum = 0, fg = 1; char c = getchar();
    	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
    	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    	return fg * sum;
    }
    
    typedef set<pii>::iterator It;
    
    const int maxn = 5e4 + 10;
    
    int n, m, cnt, ans, Begin[maxn], Next[maxn << 1], To[maxn << 1], w[maxn << 1], e ;
    
    void Add(int x, int y, int z) { To[++e] = y, Next[e] = Begin[x], Begin[x] = e, w[e] = z; }
    
    int S[maxn], len[maxn];
    bool v[maxn];
    set<pii> Set;
    
    void dfs(int now, int f) {
    
    	for (int i = Begin[now]; i + 1; i = Next[i]) {
    		int son = To[i];
    		if (son == f) continue;
    		dfs(son, now);
    	}
    
    	Set.clear(), S[0] = 0;
    	for (int i = Begin[now]; i + 1; i = Next[i]) {
    		int son = To[i];
    		if (son == f) continue;
    		S[++S[0]] = len[son] + w[i];
    	}
    
    	sort(S + 1, S + S[0] + 1);
    	It lst = Set.begin();
    	for (int i = 1; i <= S[0]; i++) {
    		if (S[i] >= ans) { ++cnt, v[i] = 1; continue; }
    		lst = Set.insert(lst, (pii){S[i], i}), v[i] = 0;
    	}
    
    	for (int i = 1; i <= S[0]; i++) {
    		if (v[i]) continue;
    		It pos = Set.lower_bound((pii){ans - S[i], 0});
    		if (pos == Set.end() || (*pos) == (pii){S[i], i}) continue;
    		v[i] = 1, v[pos->snd] = 1, ++cnt;
    		Set.erase((pii){S[i], i}), Set.erase(pos);
    	}
    
    	len[now] = 0;
    	for (int i = S[0]; i >= 1; i--)
    		if (!v[i]) {
    			len[now] = S[i];
    			break;
    		}
    }
    
    bool check(int mid) {
    	ans = mid, cnt = 0;
    	dfs(1, 0);
    	return cnt >= m;
    }
    
    int main() {
    	freopen("track.in", "r", stdin);
    	freopen("track.out", "w", stdout);
    
    	e = -1, memset(Begin, -1, sizeof Begin);
    	n = read(), m = read();
    	for (int i = 1; i < n; i++) {
    		int x = read(), y = read(), z = read();
    		Add(x, y, z), Add(y, x, z);
    	}
    
    	int l = 1, r = 5e8;
    	while (l <= r) {
    		int mid = (l + r) >> 1;
    		if (check(mid)) l = mid + 1;
    		else r = mid - 1;
    	}
    
    	cout << r << endl;
    
    	return 0;
    }
    

    Summary

    这道题一定要注意(set)的使用方法 考场上写挂了

    要先用一个数组把(set)里的元素存起来,然后去遍历这个数组。在(set)中删除一个元素的时候,直接对这个元素的编号打一个(vis)标记就可以了

  • 相关阅读:
    Hibernate(十三)迫切内连接fetch
    SQL多表联合查询(交叉连接,内连接,外连接)
    Hibernate入门(十二)离线条件检索
    Java基础IO流(二)字节流小案例
    Java基础IO流(一)
    Hibernate入门(十一)多对多案例
    mysql下载安装及常见问题
    数据库表数据恢复
    linux的自有(内置)服务
    linux下的别名机制
  • 原文地址:https://www.cnblogs.com/xunzhen/p/10016415.html
Copyright © 2020-2023  润新知