P2495 [SDOI2011]消耗战

Posted olinr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2495 [SDOI2011]消耗战相关的知识,希望对你有一定的参考价值。

(color{#0066ff}{ 题目描述 })

在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。

侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

(color{#0066ff}{输入格式})

第一行一个整数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,表示资源丰富岛屿的编号。

(color{#0066ff}{输出格式})

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

(color{#0066ff}{输入样例})

10
1 5 13
1 9 6
2 1 19
2 4 8
2 3 91
5 6 8
7 5 4
7 8 31
10 7 9
3
2 10 6
4 5 7 8 3
3 9 4 6

(color{#0066ff}{输出样例})

12
32
22

(color{#0066ff}{数据范围与提示})

对于10%的数据,2<=n<=10,1<=m<=5,1<=ki<=n-1

对于20%的数据,2<=n<=100,1<=m<=100,1<=ki<=min(10,n-1)

对于40%的数据,2<=n<=1000,m>=1,sigma(ki)<=500000,1<=ki<=min(15,n-1)

对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

(color{#0066ff}{ 题解 })

对于每个询问,对关键点建立虚树

值得注意的是,我们的目的是不让它们与1相连, 而不是与虚树的根相连

所以1也要在虚树中

建好虚树,开始DP(因为要删的边权最小,所以建虚树的边是原树是上最小的边, 用倍增)

设f[i]为在虚树中,让以i为根的子树的关键点到不了i的最小代价

如果当前点是关键点,那么在它的子树中割边很显然是没意义的,所以直接返回(dp[i] = inf)

否则(f[u] = sum{min(f[v], dis_{u,v})})

最后再1处收集ans即可

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
struct node {
    int to;
    LL dis;
    node *nxt;
    node(int to = 0, LL dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
    void *operator new (size_t) {
        static node *S = NULL, *T = NULL;
        return (S == T) && (T = (S = new node[1024]) + 1024), S++;
    }
};
const int maxn = 2e5 + 6e4;
const LL inf = 999999999999999LL;
node *head[maxn], *h[maxn];
int dep[maxn], f[maxn][28], a[maxn], st[maxn], top, g[maxn], dfn[maxn];
LL d[maxn][28], dp[maxn];
int n, m, cnt;
bool is[maxn];

void add(int from, int to, LL dis, node **hh) {
    hh[from] = new node(to, dis, hh[from]);
}
void dfs(int x, int fa) {
    dfn[x] = ++cnt;
    f[x][0] = fa;
    dep[x] = dep[fa] + 1;
    for(node *i = h[x]; i; i = i->nxt) 
        if(i->to != fa) dfs(i->to, x), d[i->to][0] = i->dis;
}
void LCA(int x, int y, LL &dis, int &id) {
    id = 0;
    dis = inf;
    if(dep[x] < dep[y]) std::swap(x, y);
    for(int i = 26; i >= 0; i--) if(dep[f[x][i]] >= dep[y]) dis = std::min(dis, d[x][i]), x = f[x][i];
    if(x == y) return (void)(id = x);
    for(int i = 26; i >= 0; i--) if(f[x][i] != f[y][i]) dis = std::min(dis, std::min(d[x][i], d[y][i])), x = f[x][i], y = f[y][i];
    dis = std::min(dis, std::min(d[x][0], d[y][0]));
    id = f[x][0];
}

void DP(int x) {
    if(is[x]) return (void)(dp[x] = inf);
    dp[x] = 0;
    for(node *i = head[x]; i; i = i->nxt)
        DP(i->to), dp[x] += std::min(dp[i->to], i->dis);
}
void clr(int x) {
    for(node *i = head[x]; i; i = i->nxt)
        clr(i->to);
    head[x] = NULL;
}

int main() {
    n = in();
    int x, y, z;
    for(int i = 1; i < n; i++) {
        x = in(), y = in(), z = in();
        add(x, y, z, h), add(y, x, z, h);
    }
    memset(d, 0x7f, sizeof d);
    dfs(1, 0);
    for(int j = 1; j <= 26; j++)
        for(int i = 1; i <= n; i++) {
            f[i][j] = f[f[i][j - 1]][j - 1];
            d[i][j] = std::min(d[i][j - 1], d[f[i][j - 1]][j - 1]);
        }
    for(m = in(); m --> 0;) {
        int num = in();
        for(int i = 1; i <= num; i++) a[i] = in(), is[a[i]] = true;
        a[++num] = 1;
        cnt = 0;
        std::sort(a + 1, a + num + 1, [](const int &x, const int &y) { return dfn[x] < dfn[y]; });
        st[top = 1] = a[1];
        LL D;
        int lca, id;
        for(int i = 2; i <= num; i++) {
            int now = a[i];
            LCA(now, st[top], D, lca);
            while(top > 1 && dep[lca] <= dep[st[top - 1]]) {
                LCA(st[top], st[top - 1], D, id);
                add(st[top - 1], st[top], D, head);
                top--;
            }
            if(lca != st[top]) {
                LCA(st[top], lca, D, id);
                add(lca, st[top], D, head);
                st[top] = lca;
            }
            st[++top] = now;
        }
        while(top > 1) {
            LCA(st[top], st[top - 1], D, id);
            add(st[top - 1], st[top], D, head);
            top--;
        }
        DP(st[1]);
        printf("%lld
", dp[st[1]]);
        clr(st[1]);
        for(int i = 1; i <= num; i++) is[a[i]] = false;
    }
    return 0;
}

以上是关于P2495 [SDOI2011]消耗战的主要内容,如果未能解决你的问题,请参考以下文章

luogu P2495 [SDOI2011]消耗战

P2495 [SDOI2011]消耗战

Luogu P2495 [SDOI2011]消耗战

P2495 [SDOI2011]消耗战 (虚树)

P2495 [SDOI2011]消耗战

洛谷 P2495 [SDOI2011]消耗战