zoukankan      html  css  js  c++  java
  • 【NOIP2012】疫情控制

    题目链接

    【NOIP2012】疫情控制

    做法

    这道题显然可以二分答案。
    考虑构造解。对于每个军队,当它不能到达首都时,它的深度显然越小越好。对于可以到达首都的军队,它有两种决策:退回上一个位置、去填补其他子树的最上面节点。可以用倍增维护。
    考虑一种特殊数据:

    5
    1 2 5
    1 3 2
    1 4 5
    4 5 1000000000
    3 3 4 5
    

    它的构造方案是 $ 5 o 5, 4 o 3, 3 o 2 $ ,答案是 $ 7 $ 。
    考虑一个能到达根节点的军队(从 $ root $ 的儿子 $ x $ 来),若它的步数还能回到上一个位置(并非时光倒流),则直接将它放在根节点考虑没有影响;否则它要么去一个 $ dis(root, x) geq dis(root, y), y in son(root) $ 的地方,要么待在 $ x $ 。这部分可以用 $ multiset $ 维护。
    最后贪心地从首都分配军队。

    #include <bits/stdc++.h>
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define pb push_back
    #define mp make_pair
    #define fst first
    #define snd second
    using namespace std;
    typedef long long ll;
    const int N = 300010;
    int n, m, cnt[N],fa[19][N], fr[N], flag[N]; ll ans = -1;
    struct P {
    	int to; ll w; P(int To = 0, ll W = 0) : to(To), w(W) {}
    	inline bool operator<(const P &yy)const { return w > yy.w; }
    }; vector<P> e[N];
    ll depw[N];
    ll a[N], b[N]; int la, lb;
    vector<ll> hv[N];
    multiset<ll> st;
    
    template<typename T> inline void gi(T &x) {
    	x = 0; register char c = getchar(), pre = 0;
    	for(; c < '0' || c > '9'; pre = c, c = getchar());
    	for(; c >= '0' && c <= '9'; c = getchar()) x = 10ll * x + (c ^ 48);
    	if(pre == '-') x = -x;
    }
    void dfs(int u, int ff, int anc) {
    	if(ff == 1) anc = u; fr[u] = anc, fa[0][u] = ff;
    	rep(i, 1, 18) fa[i][u] = fa[i - 1][fa[i - 1][u]];
    	for(auto v : e[u]) if(v.to != ff) depw[v.to] = depw[u] + v.w, dfs(v.to, u, anc);
    }
    void solve(int u, int ff) {
    	if(flag[u] || e[u].size() == 1) return ;
    	int cnt = 0, now = 0;
    	for(auto v : e[u]) if(v.to != ff) ++cnt, solve(v.to, u), now += flag[v.to];
    	if(cnt == now) flag[u] = 1;
    }
    bool check(ll x) {
    	la = lb = 0; rep(i, 1, n) flag[i] = 0, hv[i].clear(); st.clear();
    	if(cnt[1]) { rep(i, 1, cnt[1]) a[++la] = x; }
    	rep(i, 2, n) if(cnt[i]) {
    		if(depw[i] <= x)
    			rep(j, 1, cnt[i]) {
    				if(depw[fr[i]] <= x - depw[i]) a[++la] = x - depw[i];
    				else hv[fr[i]].pb(x - depw[i]);
    			}
    		else {
    			int anc = i; ll tmp = x;
    			per(j, 18, 0)
    				if(fa[j][anc] && depw[anc] - depw[fa[j][anc]] <= tmp)
    					tmp -= depw[anc] - depw[fa[j][anc]], anc = fa[j][anc];
    			flag[anc] = 1;
    		}
    	}
    	for(auto v : e[1]) {
    		solve(v.to, 1), sort(hv[v.to].begin(), hv[v.to].end(), greater<ll>());
    		if(!flag[v.to]) {
    			multiset<ll>::iterator it = st.lower_bound(v.w);
    			if(it != st.end() && (!hv[v.to].size() || *it <= hv[v.to].back()))
    				st.erase(it), flag[v.to] = 1;
    			else if(hv[v.to].size()) hv[v.to].pop_back(), flag[v.to] = 1;
    		}
    		for(auto i : hv[v.to]) st.insert(i); if(!flag[v.to]) b[++lb] = v.w;
    	}
    	sort(a + 1, a + la + 1), sort(b + 1, b + lb + 1);
    	int p1 = 1, p2 = 1;
    	for(; p1 <= la && p2 <= lb; ++p1, ++p2) {
    		for(; p1 <= la && a[p1] < b[p2]; ++p1);
    		if(p1 > la) break;
    	}
    	return p2 > lb;
    }
    int main() {
    	gi(n);
    	rep(i, 2, n) { int x, y, z; gi(x), gi(y), gi(z), e[x].pb(P(y, z)), e[y].pb(P(x, z)); }
    	sort(e[1].begin(), e[1].end());
    	gi(m); rep(i, 1, m) { int x; gi(x), ++cnt[x]; }
    	dfs(1, 0, 0);
    	ll l = 0, r = 1e16, mid;
    	for(; l <= r;) mid = (l + r) / 2, check(mid) ? (ans = mid, r = mid - 1) : l = mid + 1;
    	printf("%lld
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    【西瓜书】周志华《机器学习》学习笔记与习题探讨(一)
    01-线性回归算法
    NumPy 字符串函数
    Numpy函数分类
    Excel一对多查询(index+small+if)
    支付机构MRC模
    数据分析方法论
    窗口函数/解析函数
    数据分析
    底层逻辑
  • 原文地址:https://www.cnblogs.com/daniel14311531/p/11611684.html
Copyright © 2011-2022 走看看