• luoguP2495 [SDOI2011]消耗战


    https://www.luogu.org/problemnew/show/P2495

    Dp 方程很显然

    设 Dp[u] 表示——使 u 不与其子树中任意一个关键点联通的最小代价

    设 w[a, b] 表示 a 与 b 之间的边的权值。

    • 若 son[i] 不是关键点,Dp[u] = Dp[u] + min{Dp[son[i]],w[u][son[i]]}
    • 若 son[i] 是关键点,Dp[u] = Dp[u] + w[u][son[i]]

    但这样复杂度很显然是不对的,所以我们考虑虚树

    什么,你还不会虚树?那就去跟 zzq 学吧 https://www.cnblogs.com/zzqsblog/p/5560645.html

    我们发现 k 的总和与 n 同级,所以用虚树优化这个 Dp,建出虚树,在虚树上 Dp 即可

    #include <bits/stdc++.h>
    #define X first
    #define Y second
    #define mp make_pair
    using namespace std;
    
    typedef long long ll;
    const int N = 250000 + 5, LG2 = 18;
    
    vector < pair <int, int> > G[N], G2[N];
    int pre[N][LG2 + 1], dep[N], mx[N][LG2 + 1], id[N], dfn;
    int n, m, k, h[N], sta[N], len, MX;
    ll f[N];
    bool book[N];
    
    void init(int u, int fa) {
    	pre[u][0] = fa; dep[u] = dep[fa] + 1; id[u] = ++dfn;
    	for(int i = 1; i <= LG2; i++) {
    		pre[u][i] = pre[pre[u][i - 1]][i - 1];
    		mx[u][i] = min(mx[u][i - 1], mx[pre[u][i - 1]][i - 1]);
    	}
    	for(vector < pair <int, int> > :: iterator it = G[u].begin(); it != G[u].end(); it++) {
    		int v = it -> X; if(v != fa) mx[v][0] = it -> Y, init(v, u);
    	}
    }
    
    int LCA(int x, int y) {
    	MX = INT_MAX;
    	if(dep[x] > dep[y]) swap(x, y);
    	for(int i = LG2; i >= 0; i--)
    		if(dep[pre[y][i]] >= dep[x])
    			MX = min(MX, mx[y][i]), y = pre[y][i];
    	if(x == y) return x;
    	for(int i = LG2; i >= 0; i--)
    		if(pre[x][i] != pre[y][i]) {
    			MX = min(MX, min(mx[x][i], mx[y][i]));
    			x = pre[x][i], y = pre[y][i];
    		}
    	return pre[x][0];
    }
    
    bool cmp(int x, int y) {return id[x] < id[y];}
    
    void DP(int u) {
    	f[u] = 0;
    	for(vector < pair <int, int> > :: iterator it = G2[u].begin(); it != G2[u].end(); it++) {
    		int v = it -> X; DP(v);
    		if(book[v]) f[u] += it -> Y;
    		else f[u] += min(f[v], (ll)it -> Y);
    	}
    }
    
    int main() {
    	cin >> n;
    	for(int i = 1; i < n; i++) {
    		int a, b, c;
    		scanf("%d %d %d", &a, &b, &c);
    		G[a].push_back( mp(b, c) );
    		G[b].push_back( mp(a, c) ); 
    	}
    	init(1, 0); cin >> m;
    	while(m--) {
    		scanf("%d", &k);
    		for(int i = 1; i <= k; i++) {
    			scanf("%d", &h[i]);
    			book[h[i]] = 1;
    		}
    		sort(h + 1, h + k + 1, cmp);
    		sta[len = 1] = 1; G2[1].clear();
    		for(int i = 1; i <= k; i++) {
    			if(h[i] == 1) continue;
    			int lca = LCA(h[i], sta[len]);
    			if(lca != sta[len]) {
    				while(id[lca] < id[sta[len - 1]]) {
    					LCA(sta[len - 1], sta[len]);
    					G2[sta[len - 1]].push_back( mp(sta[len], MX) );
    					len--;
    				}
    				if(id[lca] > id[sta[len - 1]]) {
    					G2[lca].clear();
    					LCA(lca, sta[len]);
    					G2[lca].push_back( mp(sta[len], MX) );
    					sta[len] = lca;
    				} else LCA(lca, sta[len]), G2[lca].push_back( mp(sta[len], MX) ), len--;
    			}
    			G2[h[i]].clear(); sta[++len] = h[i];
    		}
    		for(int i = 1; i < len; i++) LCA(sta[i], sta[i + 1]), G2[sta[i]].push_back( mp(sta[i + 1], MX) );
    		DP(1); printf("%lld
    ", f[1]);
    		for(int i = 1; i <= k; i++) book[h[i]] = 0;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Python之struct模块浅谈
    看头发知健康
    ZeroMQ:云计算时代最好的通讯库
    粗盐热敷疗法经验汇总
    百度2011校招笔试算法题一
    new/delete 和malloc/free 的区别一般汇总
    Trie字典树
    百度2012校招笔试题之全排列与组合
    百度2011校招笔试算法题二
    执行程序的内存分布总结
  • 原文地址:https://www.cnblogs.com/LJC00118/p/9612518.html
Copyright © 2020-2023  润新知