zoukankan      html  css  js  c++  java
  • BZOJ 2286 [Sdoi2011]消耗战 (虚树模板题)

    题意

    (n)个点形成一棵树,边有权值。(m)次询问,每次询问,给出(k)个关键点,问把这些关键点都与(1)号节点断开(不连通)的最小断边的权值和。保证(1)号节点不是关键点。
    (n,mle 250000,sum kle500000)

    题解

    考虑只有一次询问怎么做。

    直接树形(dp)就行了。用(dp[u])表示把(u)这棵子树断开的最小代价。对于关键点必须断(u)到父亲的边。否则要么断当前点到根的路径上边权最小值,要么断子树内的边(代价为:子树内有关键点的儿子的(dp)值之和)。

    我们可以把(1)号根节点到父亲的边权设为(+infty),那么(dp[1])就是答案。

    然后考虑多组数据,发现(sum kle 500000),显然每次我们需要(O(k)/O(klog k))的算法。

    那么就只需要建虚树(DP)就完事了,每次(O(klog k+k))。这道题还有一个性质,就是虚树中关键点子树中的关键点可忽略。建虚树的时候没有必要建出来。

    CODE

    #include <cstdio>
    #include <vector>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    inline void read(int &x) {
    	int flg = 1; char ch; while(!isdigit(ch=getchar())) if(ch=='-')flg=-flg;
    	for(x = ch-'0'; isdigit(ch=getchar()); x = x*10+ch-'0'); x *= flg;
    }
    typedef long long LL;
    #define il inline
    const int MAXN = 250005;
    
    int n, q, m;
    int fir[MAXN], to[MAXN<<1], nxt[MAXN<<1], wt[MAXN<<1], cnt; LL val[MAXN];
    il void link(int u, int v, int w) {
    	to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; wt[cnt] = w;
    	to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt; wt[cnt] = w;
    }
    
    int sz[MAXN], fa[MAXN], top[MAXN], dep[MAXN], son[MAXN], dfn[MAXN], tmr;
    void dfs1(int u, int ff) {
    	dep[u] = dep[fa[u] = ff] + (sz[u] = 1);
    	for(int i = fir[u], v; i; i = nxt[i])
    		if((v=to[i]) != ff) {
    			val[v] = min(1ll*wt[i], val[u]);
    			dfs1(v, u); sz[u] += sz[v];
    			if(sz[v] > sz[son[u]]) son[u] = v;
    		}
    }
    
    void dfs2(int u, int tp) {
    	top[u] = tp; dfn[u] = ++tmr;
    	if(son[u]) dfs2(son[u], tp);
    	for(int i = fir[u], v; i; i = nxt[i])
    		if((v=to[i]) != fa[u] && v != son[u])
    			dfs2(v, v);
    }
    il int Lca(int u, int v) {
    	while(top[u] != top[v]) {
    		if(dep[top[u]] > dep[top[v]]) u = fa[top[u]];
    		else v = fa[top[v]];
    	}
    	return dep[u] > dep[v] ? v : u;
    }
    int c[MAXN], stk[MAXN], indx;
    vector<int>e[MAXN];
    il bool cmp(int i, int j) { return dfn[i] < dfn[j]; }
    il void ins(int x) {
    	if(indx == 1) { stk[++indx] = x; return; }
    	int lca = Lca(x, stk[indx]);
    	if(lca == stk[indx]) return; //此题性质
    	while(indx > 1 && dfn[stk[indx-1]] >= dfn[lca])
    		e[stk[indx-1]].push_back(stk[indx]), --indx;
    	if(lca != stk[indx]) e[lca].push_back(stk[indx]), stk[indx] = lca;
    	stk[++indx] = x;
    }
    LL dp(int u) {
    	if(!e[u].size()) return val[u];
    	LL re = 0;
    	for(int i = e[u].size()-1; i >= 0; --i)
    		re += dp(e[u][i]);
    	e[u].clear();
    	return min(re, val[u]);
    }
    int main () {
    	read(n);
    	for(int i = 1, x, y, z; i < n; ++i)
    		read(x), read(y), read(z), link(x, y, z);
    	val[1] = 1ll<<60; dfs1(1, 0); dfs2(1, 1);
    	read(q);
    	while(q--) {
    		read(m);
    		for(int i = 1; i <= m; ++i) read(c[i]);
    		sort(c + 1, c + m + 1, cmp);
    		stk[indx=1] = 1; //1号点必须放进来
    		for(int i = 1; i <= m; ++i) ins(c[i]);
    		while(indx > 1) e[stk[indx-1]].push_back(stk[indx]), --indx;
    		printf("%lld
    ", dp(1));
    	}
    }
    
  • 相关阅读:
    sql 生成开始日期到结束日期相差天数或日期
    自定义表做存储过程变量
    [转]html 移动互联网终端的javascript touch事件,touchstart, touchend, touchmove
    [转]JQuery.Ajax之错误调试帮助信息
    解决IOS safari在input focus弹出输入法时不支持position fixed的问题
    查看 存储过程的执行时间
    ListView
    android矩阵详解
    跳出圈子 “莫忘初心,方得始终”
    Eclipse使用
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12149366.html
Copyright © 2011-2022 走看看