• [CF1119F] Niyaz and Small Degrees


    [题目链接]

    https://codeforces.com/contest/1119/problem/F

    [题解]

    首先考虑一个朴素的做法。

    (dp_{u , 0 / 1}) 表示以 (u) 为根的子树 , ((u , fa(u))) 这条边割 / 不割的最小代价。

    忽略 (X) 的限制 , 首先令 (dp_{u , 0 / 1} = sum_{v}{max{dp_{v , 0} , dp_{v , 1} + w}})

    不妨称 (dp_{v , 1} + w leq dp_{v , 0}) 的儿子 (v) 叫 "好儿子" , 其余叫"坏儿子"。 那么要做的就是把 (max{0 , deg_{u} - cnt - X}) 个"坏儿子" 变为 "好儿子"。 把 (dp_{v , 1} + w - dp_{v , 0}) 放入一个堆中 , 取出前若干小的即可。

    这样时间复杂度是 (O(N^2logN)) 的。

    考虑优化 , 发现随着 (X) 增大 , 那些度数不超过 (X) 的点 (u) 就可以直接删去了 , 只需在与其相邻的点 (v) 的堆中加入 (w(u , v)) 即可。 然后再对剩下的点做一遍树形 (DP)

    因为 (sum{X}sum_{i}{[deg_{i} > X]} = sum{deg_{i}} = 2N - 2) , 所以更新次数不超过 (O(N))

    故时间复杂度 : (O(NlogN))

    [代码]

    #include <bits/stdc++.h>
     
    using namespace std;
     
    typedef long long LL;
     
    typedef pair < int , int > pii;
    #define mp make_pair
     
    const int MN = 3e5 + 5;
     
    int N , X;
    vector < pii > E[MN];
    int deg[MN] , vis[MN];
    LL dp[MN][2];
    vector < LL > del , tmp;
    pii D[MN];
     
    inline void AddEdge(int u , int v , int w) {
    	E[u].emplace_back(mp(v , w));
    	++deg[u];
    }
    inline bool cmp(pair < int , int > x , pair < int , int > y) {
    	return deg[x.first] > deg[y.first];
    }
    struct Heap {
    	int sz; LL sum;
    	priority_queue < LL > a , b;
    	inline void push(LL x) { a.push(x); ++sz; sum += (LL) x; }
    	inline void erase(LL x) { b.push(x); --sz; sum -= (LL) x; }
    	inline void check() { while (!a.empty() && !b.empty() && a.top() == b.top()) { a.pop(); b.pop(); } }
    	inline LL top() { check(); return a.top(); }
    	inline void pop() { check(); --sz; sum -= (LL) a.top(); a.pop(); }
    	inline int size() { return sz; }
    } H[MN];
     
    inline void die(int u) {
    	for (auto to : E[u]) {
    		int v = to.first , w = to.second;
    		if (deg[v] <= X) break;
    		H[v].push(w);
    	}
    }
    inline void dfs(int u , int fa = 0) {
    	vis[u] = X;
    	int need = deg[u] - X;
    	LL res = 0;
    	for (; H[u].size() > need; H[u].pop());
    	for (auto to : E[u]) {
    		int v = to.first , w = to.second; 
    		if (deg[v] <= X) break; if (v == fa) continue; 
    		dfs(v , u);
    	}
    	tmp.clear() , del.clear();
    	for (auto to : E[u]) {
    		int v = to.first , w = to.second; if (v == fa) continue; 
    		if (deg[v] <= X) break;
    		LL x = dp[v][1] + w - dp[v][0];
    		if (x <= 0) { --need; res += dp[v][1] + w; continue; }
    		res += dp[v][0]; H[u].push(x); del.emplace_back(x);
    	}
    	for (; H[u].size() && H[u].size() > need; H[u].pop()) tmp.emplace_back(H[u].top());
    	dp[u][0] = res + H[u].sum;
    	for (; H[u].size() && H[u].size() > need - 1; H[u].pop()) tmp.emplace_back(H[u].top());
    	dp[u][1] = res + H[u].sum;
    	for (int i : tmp) H[u].push(i);
    	for (int i : del) H[u].erase(i);
    }
     
    int main() {
    	
    	scanf("%d" , &N); LL sum = 0;
    	for (int i = 1; i < N; ++i) {
    		int u , v , w;
    		scanf("%d%d%d" , &u , &v , &w);
    		AddEdge(u , v , w); AddEdge(v , u , w);
    		sum += 1LL * w;
    	}
    	printf("%lld" , sum);
    	for (int i = 1; i <= N; ++i)
    		D[i] = mp(deg[i] , i) , sort(E[i].begin() , E[i].end() , cmp);
    	sort(D + 1 , D + N + 1);
    	int cur = 1;
    	for (X = 1; X < N; ++X) {
    		while (cur <= N && D[cur].first == X) die(D[cur].second) , ++cur;
    		LL ans = 0;
    		for (int j = cur; j <= N; ++j) {
    			int v = D[j].second;
    			if (vis[v] == X) continue;
    			dfs(v) , ans += 1LL * dp[v][0];
    		}
    		printf(" %lld" , ans);
    	}
    	printf("
    ");
    	return 0; 
    }
  • 相关阅读:
    如何实现Iframe透明
    ListView(未完)
    我又回来了
    前言
    代码重用and思路重用
    图片上传
    千万数量级分页存储过程
    MSSQL中,将text,ntext转换为int型数据
    优秀的回复,来自圣殿骑士
    SqlDataSource控件
  • 原文地址:https://www.cnblogs.com/evenbao/p/14039384.html
Copyright © 2020-2023  润新知