[虚树dp] bzoj2286: Sdoi2011消耗战

原创 2016年08月30日 17:09:45

Sdoi2011消耗战

题意:

给一棵带权树,1为根,q次询问,每次询问给出k个特殊点,要求去掉一些边,代价是权值,让点1不能到达k里的任意一个,输出最小代价。

题解:

首先考虑单次询问,容易想到dp[x]表示让1不能到x子树里的特殊点的代价。
当x是特殊点的时候,显然有dp[x]= min(path[x]),表示x到根节点的最小的那条边,因为一定要去掉x。
当x不是的时候,dp[x]=min(path[x], sum(dp[son(x)])),表示要么去掉x,要么去掉x所有儿子的子树的特殊点。
答案就是dp[1]。
现在变成多次询问,但是总的特殊点不超过105,需要用到虚树的技巧。
看了几篇blog学了一下虚树,这篇的写法很不错。
照着思路写就行了,代码还是比较容易的。

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+5;
typedef long long ll;
typedef pair<int,ll> edg;
const ll inf = ~0ull>>5;
vector<edg>G[N];
vector<int>qry, GG[N];
ll cost[N];
int dep[N], fa[N][25], in[N], cnt = 0;
int st[N], top;
bool vis[N];
inline bool cmp(const int& a, const int& b){
    return in[a] < in[b];
}
void dfs1(int rt, int f){
    in[rt] = ++cnt;
    for(int i = 1; i < 25; ++i){
        if(dep[rt] < (1<<i)) break;
        fa[rt][i] = fa[fa[rt][i-1]][i-1];
    }
    for(int i = 0; i < G[rt].size(); ++i){
        int v = G[rt][i].first;
        if(v == f) continue;
        cost[v] = min(cost[rt], G[rt][i].second);
        dep[v] = dep[rt]+1;
        fa[v][0] = rt;
        dfs1(v, rt);
    }
}
int lca(int a, int b){
    if(dep[a] < dep[b]) swap(a, b);
    int delt = dep[a] - dep[b];
    for(int i = 0; i < 25; ++i){
        if(delt&(1<<i)) a = fa[a][i];
    }
    for(int i = 24; i >= 0; --i){
        if(fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
    }
    if(a != b) return fa[a][0];
    else return a;
}
void add(int a, int b){
    if(a == b) return;
    GG[a].push_back(b);
}
ll dp[N];
void solve(int rt){
    dp[rt] = cost[rt];
    if(!vis[rt]){
        ll sum = 0;
        for(int i = 0; i < GG[rt].size(); ++i){
            solve(GG[rt][i]);
            sum += dp[GG[rt][i]];
        }
        dp[rt] = min(dp[rt], sum);
    }
}
int main(){
    int n;
    scanf("%d", &n);
    for(int a, b, c, i = 1; i < n; ++i){
        scanf("%d%d%d", &a, &b, &c);
        G[a].push_back(edg(b, c));
        G[b].push_back(edg(a, c));
    }
    cost[1] = inf;
    dfs1(1, 1);
    int q;
    scanf("%d", &q);
    while(q--){
        qry.clear();
        int k;
        scanf("%d", &k);
        for(int a, i = 0; i < k; ++i){
            scanf("%d", &a);
            qry.push_back(a);
            vis[a] = 1;
        }
        sort(qry.begin(), qry.end(), cmp);
        int len = qry.size();
        for(int i = 1; i < len; ++i){
            qry.push_back(lca(qry[i], qry[i-1]));
        }
        qry.push_back(1);
        sort(qry.begin(), qry.end(), cmp);
        qry.erase(unique(qry.begin(), qry.end()), qry.end());
        int top = 0;
        for(int i = 0; i < qry.size(); ++i){
            while(top > 0 && lca(st[top], qry[i]) != st[top]) top--;
            if(top) add(st[top], qry[i]);
            st[++top] = qry[i];
        }
        solve(1);
        for(int i = 0; i < qry.size(); ++i) GG[qry[i]].clear(), vis[qry[i]] = 0;
        printf("%lld\n", dp[1]);
    }
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

[BZOJ2286][Sdoi2011]消耗战(虚树+lca+树形dp)

题目描述传送门题解裸的dp可以得到20pts 令f(i)表示将i点子树中所有关键点割掉的最小代价 那么若i为关键点,f(i)=i的父边权;若i不是关键点,f(i)=所有儿子的f之和 与 i的父边权...

[BZOJ2286][SDOI2011]消耗战(虚树+树形DP)

看到题目的条件,容易想到是一道树形DP题,设f[u]f[u]为让uu不能到达uu的子树内(不包括uu)的任意关键点(能源丰富的岛屿)的最小代价。如果uu是叶子节点,那么f[u]=0f[u]=0。 转...

虚树+树形dp bzoj2286【Sdoi2011】 消耗战

题目大意: 给定一棵根节点为1的带边权的树。 每次给定一些点,求把这些点与树根断开的最小花费。题目分析: 我们可以O(n)处理出每个点与根断开的最小花费,即该节点到根的路径上的最小边权。 然后...
  • Todobe
  • Todobe
  • 2017年03月17日 08:48
  • 141

bzoj 2286 SDOI2011 消耗战 虚树dp

题意就不说了。分析: 这题其实就是让你切代价最小的边使得k个点与根不联通,可以设一个和暴力的DP。 设val[i]表示要让i和根断开的代价。 很显然有f[i]=min(Σf[son],val[i]) ...

BZOJ2286: [Sdoi2011]消耗战 虚树

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2286 题解:看数据范围以及结合题目要求显然可以用虚树,然后建出来虚树在上面DP就好了 建虚...
  • Oakley_
  • Oakley_
  • 2017年03月14日 22:38
  • 218

BZOJ 2286: [Sdoi2011]消耗战

Description在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜...

bzoj2286 [Sdoi2011]消耗战

Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我...

BZOJ 2286 SDOI 2011 消耗战 LCA单调性

题目大意:给出一棵有根树,n组询问,每一组询问给出树上的一些关键点,问割掉一些边使得根与这些点不联通的最小花费是多少。总询问的点不超过O(n)。 思路:基础思路是每一次询问做一次O(n)的D...

bzoj 2286: [Sdoi2011]消耗战

题目大意,切掉若干条边,使根不能到指定的点,代价最小,多次询问。 这篇东西不错:点击打开链接 虚树的模板题,注意卡时,在建虚树前先将一条链的点删剩一个,每个点记录到根最短的边边权。 貌似数据有误...

【SDOI2011】bzoj2286 消耗战

虚树+树形dp
  • sdfzyhx
  • sdfzyhx
  • 2017年03月27日 17:01
  • 169
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[虚树dp] bzoj2286: Sdoi2011消耗战
举报原因:
原因补充:

(最多只允许输入30个字)