• BZOJ 2286: [Sdoi2011]消耗战 虚树


    Description

    在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
    侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

    Input

    第一行一个整数n,代表岛屿数量。

    接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。

    第n+1行,一个整数m,代表敌方机器能使用的次数。

    接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

    Output

    输出有m行,分别代表每次任务的最小代价。

    题解: 

    此题中 $n$ 最大是 $250000$,然而询问次数确是 $10^5$ 级别的
    朴素做法是 $O(mn)$ 的树形DP,效率太低
    虽然询问次数非常多,然而 $sum k$ 却只有 $5 imes 10^5$
    我们引入虚树
    虚树就是每次只保留有用的节点,即关键节点与它们之间的 $LCA$
    先对所有关键点按照在原树中 $dfs$ 序前后排一下序
    开一个栈 $S$ 来存储一条深度依次递增的链(注意,$S$ 存的是链)
    考虑每次新扩展一个关键节点 $x$
    (1) 栈中元素小于等于 $1$ 个,直接加入即可
    (2) 令 $lca$ 表示 $LCA(S_{top},x)$
    若 $lca=S_{top}$, 那么 $x$ 与 $S_{top}$ 以及栈中的链还是会构成一条链,没有分叉,直接将 $x$ 加入栈中即可
    若 $lca eq S_{top}$ ,那么说明 $S_{top}$ 及其子树已全部扩展完毕,我们需要一步一步退栈
    while(top > 1 && dep[S[top - 1]] >= dep[lca]) add_edge(S[top - 1], S[top]), --top; 
    if(S[top] != lca) add_edge(lca, S[top]), S[top] = lca; 
    S[++top] = x; 
    
    将 $S_{top}$ 到 $lca$ 这条链之间的节点全部连边并退栈
    条件是 $S_{top-1}$ 的深度要大于等于 $lca$ 的深度
    由于是大于等于,所以如果 $lca$ 在栈中的话最后 $S_{top}$ 一定会等于 $lca$,那么就无需加入 $lca$
    如果 $lca$ 不等于 $S_{top}$ 的话,那么一定是 $S_{top}$ 与 $S_{top-1}$ 之间夹着 $lca$ ,直接由 $lca$ 向 $S_{top}$ 连一条边,并将栈顶改为 $lca$ 即可
     
    #include <bits/stdc++.h>
    #define setIO(s) freopen(s".in", "r", stdin) 
    #define maxn 500004 
    #define LOG 23 
    #define inf 100000000000
    #define ll long long 
    using namespace std; 
    vector <int> G[maxn];  
    int edges, tim, n, top; 
    int hd[maxn], to[maxn << 1], nex[maxn << 1], val[maxn << 1];  
    int dfn[maxn], f[LOG][maxn], arr[maxn], S[maxn], mk[maxn], dep[maxn]; 
    ll mn[maxn];  
    inline void addedge(int u, int v, int c) 
    {
    	nex[++edges] = hd[u], hd[u] = edges, to[edges] = v, val[edges] = c; 
    }
    void dfs1(int u, int ff) 
    { 
    	f[0][u] = ff; 
    	for(int i = 1; i < 22; ++i) f[i][u] = f[i - 1][f[i - 1][u]]; 
    	dep[u] = dep[ff] + 1, dfn[u] = ++tim; 
        for(int i = hd[u]; i ; i = nex[i])
        {
        	int v = to[i]; 
        	if(v == ff) continue; 
        	mn[v] = min(mn[u], 1ll*val[i]); 
        	dfs1(v, u); 
        }
    }
    inline int LCA(int a, int b)
    {
    	if(dep[a] > dep[b]) swap(a, b); 
    	if(dep[a] != dep[b])
    	{
    		for(int i = 21; i >= 0; --i) if(dep[f[i][b]] >= dep[a]) b = f[i][b];  
    	}
        if(a == b) return a; 
        for(int i = 21; i >= 0; --i) if(f[i][a] != f[i][b]) a = f[i][a], b = f[i][b]; 
        return f[0][a];  
    } 
    bool cmp(int a, int b)
    {
    	return dfn[a] < dfn[b];  
    }
    inline void add_edge(int u, int v)
    { 
    	G[u].push_back(v); 
    }
    inline void insert(int x)
    {
    	if(top <= 1) 
    	{
    		S[++top] = x; 
    		return; 
    	}
    	int lca = LCA(x, S[top]); 
    	if(lca == S[top]) return; 
    	while(top > 1 && dep[S[top - 1]] >= dep[lca]) add_edge(S[top - 1], S[top]), --top; 
    	if(S[top] != lca) add_edge(lca, S[top]), S[top] = lca; 
    	S[++top] = x; 
    } 
    ll DP(int x)
    { 
    	ll sum = 0, re; 
    	for(int i = 0; i < G[x].size(); ++i)  sum += DP(G[x][i]);   
    	if(mk[x]) re = mn[x]; 
        else re = min(mn[x], sum); 
        mk[x] = 0; 
        G[x].clear(); 
        return re; 
    }
    int main() 
    { 
    	// setIO("input"); 
    	scanf("%d",&n); 
    	for(int i = 1; i < n ; ++i) 
    	{
    		int a, b, c; 
    		scanf("%d%d%d",&a,&b,&c), addedge(a, b, c), addedge(b, a, c); 
    	} 
    	dep[1] = 1, mn[1] = inf, dfs1(1, 0);      
    	int Q; 
    	scanf("%d",&Q); 
    	while(Q--)
    	{
    		int k; 
    		scanf("%d",&k);   
    		for(int i = 1; i <= k ; ++i) scanf("%d",&arr[i]);      
    		sort(arr + 1, arr + 1 + k, cmp);    
    	    S[++top] = 1;  
    	    for(int i = 1; i <= k ; ++i) insert(arr[i]), mk[arr[i]] = 1;         
    	    while(top > 0) add_edge(S[top - 1], S[top]) , --top; 
    	    printf("%lld
    ",DP(1));  
    	    for(int i = 1; i <= k ; ++i) mk[arr[i]] = 0; 
    	}
    	return 0; 
    }
    

      

      

  • 相关阅读:
    how to install VLC Player on Fedora 32 Workstation
    Centos 8.2 2004镜像地址
    Fedora 33 Workstation: x86_64 DVD ISO
    [听力/口语]每天十分鐘回音練習
    CSS设置背景色
    设置禁止子标签超出父标签产生了滚动条
    引用自定义的css或者js文件
    js 中使用typeof
    React native 之 Promise
    Mac升级系统后 Pod Install报错-不能用 解决办法
  • 原文地址:https://www.cnblogs.com/guangheli/p/11127715.html
Copyright © 2020-2023  润新知