P2495 [SDOI2011]消耗战 虚树

这是我做的第一道虚树题啊,赶脚不错.其实虚树也没什么奇怪的,就是每棵树给你一些点,让你多次查询,但是我不想每次都O(n),所以我们每次针对给的点建一棵虚树,只包含这些点和lca,然后在这棵虚树上进行树形dp,维护每个点的最小连边权值,这样的复杂度就会降低不少.这里我写了两种写法(其实都是抄的),一种是正常建树的正常做法,还有一种是不用建树,只用堆维护,模拟一棵树的操作,维护欧拉序,就是一个点有进入的编号,也有出去的编号.这样就可以不用真正建出虚树而能进行查询.

题干:

题目描述

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

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

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

输出格式:

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

输入输出样例
输入样例#1: 复制

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

输出样例#1: 复制

12
32
22

不用建树版本的代码:

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
#include<stack>
using namespace std;
#define duke(i,a,n) for(register int i = a;i <= n;i++)
#define lv(i,a,n) for(register int i = a;i >= n;i--)
#define clean(a) memset(a,0,sizeof(a))
const long long INF = 1LL << 50;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < ‘0‘ || c > ‘9‘)
        if(c == ‘-‘) op = 1;
    x = c - ‘0‘;
    while(c = getchar(), c >= ‘0‘ && c <= ‘9‘)
        x = x * 10 + c - ‘0‘;
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar(‘-‘), x = -x;
    if(x >= 10) write(x / 10);
    putchar(‘0‘ + x % 10);
}
const int N = 250010;
struct node
{
    int l,r,nxt;
    ll w;
}a[2 * N];
int lst[N],len = 0,n,m;
void add(int x,int y,ll w)
{
    a[++len].l = x;
    a[len].r = y;
    a[len].w = w;
    a[len].nxt = lst[x];
    lst[x] = len;
}
int dfin[N],cnt = 0,tr[4 * N],fa[N][22];
int dep[N],dfout[N];
ll mi[N],sum[N];
bool book[N];
stack <int> s;
bool cmp(int x,int y)
{
    int k1 = (x > 0) ? dfin[x] : dfout[-x];
    int k2 = (y > 0) ? dfin[y] : dfout[-y];
    return k1 < k2;
}
void dfs(int x)
{
    dfin[x] = ++cnt;
    for(int i = 1;fa[x][i - 1];i++)
    {
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    }
    for(int k = lst[x];k;k = a[k].nxt)
    {
        int y = a[k].r;
        if(!dfin[y])
        {
            dep[y] = dep[x] + 1;
            mi[y] = min(mi[x],a[k].w);
            fa[y][0] = x;
            dfs(y);
        }
    }
    dfout[x] = ++cnt;
    return;
}
int lca(int u,int v)
{
    if(dep[u] < dep[v])
    swap(u,v);
    int del = dep[u] - dep[v];
    for(int i = 0;del;del >>= 1,i++)
    {
        if(del & 1)
        {
            u = fa[u][i];
        }
    }
    if(u == v) return u;
    for(int i = 20;i >= 0;i--)
    {
        if(fa[u][i] != fa[v][i])
        {
            u = fa[u][i];
            v = fa[v][i];
        }
    }
    return fa[v][0];
}
void cl_st()
{
    stack <int> un;
    swap(un,s);
}
int main()
{
    read(n);
    duke(i,1,n - 1)
    {
        int x,y,w;
        read(x);read(y);read(w);
        add(x,y,w);
        add(y,x,w);
    }
    mi[1] = INF;
    dfs(1);
    read(m);
    duke(i,1,m)
    {
        int cot = 0;
        cl_st();
//        clean(tr);
        read(cot);
        duke(j,1,cot)
        {
            read(tr[j]);
            book[tr[j]] = true;
            sum[tr[j]] = mi[tr[j]];
        }
//        cout<<"A"<<endl;
        sort(tr + 1,tr + cot + 1,cmp);
        duke(j,1,cot - 1)
        {
            int lc = lca(tr[j],tr[j + 1]);
//            cout<<lc<<" "<<tr[j]<<" "<<tr[j + 1]<<endl;
            if(!book[lc])
            {
                tr[++cot] = lc;
                book[lc] = true;
            }
        }
//        cout<<"B"<<endl;
        int nc = cot;
        for(int j = 1;j <= nc;j++)
        {
            tr[++cot] = -tr[j];
        }
        if(!book[1])
        {
            tr[++cot] = 1;
            tr[++cot] = -1;
        }
        sort(tr + 1,tr + cot + 1,cmp);
//        cout<<"C"<<endl;
        duke(j,1,cot)
        {
//            cout<<cot<<endl;
            if(tr[j] > 0)
            {
                s.push(tr[j]);
            }
            else
            {
                int now = s.top();
                s.pop();
//                cout<<now<<endl;
                if(now != 1)
                {
                    int f = s.top();
                    sum[f] += min(sum[now],mi[now]);
                }
                else
                {
                    printf("%lld\n",sum[1]);
                }
                sum[now] = 0;
                book[now] = false;
            }
        }
    }
    return 0;
}

建树的代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<vector>
#include<complex>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(int i = a;i <= n;i++)
#define lv(i,a,n) for(int i = a;i >= n;i--)
#define clean(a) memset(a,0,sizeof(a))
#define mp make_pair
#define cp complex<db>
#define enter puts("")
const long long INF = 1LL << 60;
const double eps = 1e-8;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < ‘0‘ || c > ‘9‘)
        if(c == ‘-‘) op = 1;
    x = c - ‘0‘;
    while(c = getchar(), c >= ‘0‘ && c <= ‘9‘)
        x = x * 10 + c - ‘0‘;
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar(‘-‘), x = -x;
    if(x >= 10) write(x / 10);
    putchar(‘0‘ + x % 10);
}
const int N = 250010;
int n,dfn[N],tot = 0,cnt,m,_top;
ll mn[N];
int s[N];
namespace T
{
    struct node
    {
        int l,r,nxt,w;
    } a[2 * N];
    int lst[N],dep[N],f[N],tp[N],son[N],siz[N],len = 0;
    inline void add(int x,int y,int w)
    {
        a[++len].l = x;
        a[len].r = y;
        a[len].w = w;
        a[len].nxt = lst[x];
        lst[x] = len;
    }
    void dfs1(int u,int fa,int depth)
    {
        f[u] = fa;
        dep[u] = depth;
        siz[u] = 1;
        for(int k = lst[u];k;k = a[k].nxt)
        {
            int y = a[k].r;
            if(y == fa) continue;
            mn[y] = min(mn[u],(ll)a[k].w);
            dfs1(y,u,depth + 1);
            siz[u] += siz[y];
            if(son[u] == 0 || siz[son[u]] < siz[y])
            {
                son[u] = y;
            }
        }
    }
    void dfs2(int u,int t)
    {
        dfn[u] = ++cnt;
        tp[u] = t;
        if(!son[u]) return;
        dfs2(son[u],t);
        for(int k = lst[u];k;k = a[k].nxt)
        {
            int y = a[k].r;
            if(y == f[u] || y == son[u]) continue;
            dfs2(y,y);
        }
    }
    inline int lca(int x,int y)
    {
        while(tp[x] != tp[y])
        {
            if(dep[tp[x]] < dep[tp[y]]) swap(x,y);
            x = f[tp[x]];
        }
        if(dep[x] > dep[y]) swap(x,y);
        return x;
    }
}
namespace ft
{
    vector <int> v[N];
    void add(int x,int y)
    {
        v[x].push_back(y);
    }
    inline bool cmp(int a,int b)
    {
        return dfn[a] < dfn[b];
    }
    inline void ins(int x)
    {
        if(_top == 1)
        {
            s[++_top] = x; return;
        }
        int lca = T :: lca(x,s[_top]);
        // cout<<x<<" "<<s[_top]<<endl;
        if(lca == s[_top]) return;
        while(_top > 1 && dfn[s[_top - 1]] >= dfn[lca]) add(s[_top - 1],s[_top]),_top --;
        if(lca != s[_top]) add(lca,s[_top]),s[_top] = lca;
        s[++_top] = x;
    }
    ll pr(int x)
    {
        // printf("%d ",x);
        if(v[x].size() == 0) return mn[x];
        ll ans = 0;
        for(int i = 0;i < (int) v[x].size();i++)
        {
            int y = v[x][i];
            ans += pr(y);
        }
        v[x].clear();
        return min(ans,mn[x]);
    }
}
int a[N];
int main()
{
    // freopen("2495.in","r",stdin);
    read(n);
    memset(mn,0x3f,sizeof(mn));
    duke(i,1,n - 1)
    {
        int x,y,w;
        read(x);read(y);read(w);
        T :: add(x,y,w);
        T :: add(y,x,w);
    }
    T :: dfs1(1,0,1);
    T :: dfs2(1,1);
    /*duke(i,1,n)
    printf("%d ",dfn[i]);
    puts("");*/
    int x,y;
    read(m);
    while(m--)
    {
        int k;
        read(k);
        duke(i,1,k)
        read(a[i]);
        sort(a + 1,a + k + 1,ft :: cmp);
        s[_top = 1] = 1;
        duke(i,1,k)
        ft :: ins(a[i]);
        /*duke(i,1,_top)
        printf("%d ",s[i]);
        puts("");*/
        // cout<<n<<" ";
        /*duke(i,1,n)
        printf("%lld ",mn[i]);
        puts("");*/
        while(_top > 0) ft :: add(s[_top - 1],s[_top]),_top--;
        printf("%lld\n",ft :: pr(1));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/DukeLv/p/10112047.html

时间: 2024-11-09 05:50:08

P2495 [SDOI2011]消耗战 虚树的相关文章

【BZOJ2286】[Sdoi2011]消耗战 虚树

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

luogu P2495 [SDOI2011]消耗战

二次联通门 : luogu P2495 [SDOI2011]消耗战 /* luogu P2495 [SDOI2011]消耗战 虚树+树形dp 首先对原图构建出虚树 在建图的时候处理出最小值 转移即可 小技巧 在dp的过程中可以顺便把边表清空 将边结构体封装可方便的建多张图 */ #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #define INF 1

●洛谷P2495 [SDOI2011]消耗战

题链: https://www.luogu.org/problemnew/show/P2495题解: 虚树入门,树形dp 推荐博客:http://blog.csdn.net/lych_cys/article/details/50814948 代码: #include<bits/stdc++.h> #define MAXN 250005 #define INFll 0x3f3f3f3f3f3f3f3fll using namespace std; int N,Q,M; bool mark[MAX

[SDOI2011][BZOJ2286] 消耗战|虚树|树型dp|树上倍增LCA

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1040  Solved: 363[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸

【BZOJ-2286】消耗战 虚树 + 树形DP

2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁

BZOJ 2286 消耗战 (虚树+树形DP)

给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<=500000) 考虑树形DP,我们令mn[i]表示i节点无法与1节点相连切除的最小权值.显然有mn[i]=min(E(fa,i),mn[fa]).大致就是i到1的简单路径上的最小边.我们对于每个询问.把询问的点不妨称为关键点.令dp[i]表示i节点不能与子树的关键点连接切掉的最小权值.那么有,如果son[i]

P2495 [SDOI2011]消耗战

传送门 虚树DP经典题 首先有一个显然的$O(nm)$的树形DP 以 1 号节点为根 设 $f [ x ]$ 表示把节点 $x$ 子树内的资源点都与 $x$ 的父节点断开的最小代价 那么转移显然: 枚举 $x$ 的所有儿子节点 $v$,设 $x$ 到父节点的边权为 $w$ $f [ x ] = min ( w,\sum_vf[v] )$ 然后发现 $\sum_{k_i}\leq 500000$ 所以显然要搞虚树 每次DP只要把有用的节点提出来,根据它们之间的父子关系建一颗虚树 DP只要在虚树上D

洛谷 P2495 [SDOI2011]消耗战

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

虚树教程

问题引入 SDOI2011消耗战 虚树 按照朴素的做法,就是对于每一个询问都跑一边DP. \[ Dp[i] = Dp[ i ] + \min( Dp[Son],Cost[i,Son]) \,\,\,(Son不为关键点)\Dp[i] = Dp[ i ] + Cost[ i, Son ] \,\,\,(Son为关键点)\Dp[i] = INF\,\,\,(i为叶子) \] 这样时间上显然是不行的.我们需要优化一下. 注意到\(\sum k\leqslant 500000\),那么可能许多点都不是关键