luogu P2495 [SDOI2011]消耗战

二次联通门 : luogu P2495 [SDOI2011]消耗战

/*
    luogu P2495 [SDOI2011]消耗战

    虚树+树形dp

    首先对原图构建出虚树

    在建图的时候处理出最小值
    转移即可

    小技巧
    在dp的过程中可以顺便把边表清空
    将边结构体封装可方便的建多张图
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

#define INF 1e17

inline void read (int &now)
{
    register char c = getchar ();
    for (now = 0; !isdigit (c); c = getchar ());
    for (; isdigit (c); now = now * 10 + c - ‘0‘, c = getchar ());
}
#define Max 1000010

struct Edge
{
    int to, next, w;
    Edge (int _x, int _y, int _z) : to (_x), next (_y), w (_z) {}
    Edge () {}
};

int N, M;
long long value[Max];
int dfn[Max], Count;
struct Graph
{
    Edge e[Max << 1];
    int C;
    Graph () { C = 0;}

    int list[Max];
    inline void Clear ()
    {
        C = 0;
    }
    inline void Add_Edge (int from, int to, int dis)
    {
        if (from == to) return ;
        e[++ C].to = to;
        e[C].next = list[from];
        list[from] = C;
        e[C].w = dis;
    }
    inline void Add_Edge (int from, int to)
    {
        if (from == to) return ;
        e[++ C].to = to;
        e[C].next = list[from];
        list[from] = C;
    }
};

inline void swap (int &x, int &y)
{
    int now = x;
    x = y;
    y = now;
}

inline long long min (int x, long long y)
{
    return x < y ? x : y;
}

class Tree_Chain_Get
{
    private :
        Graph G;
        int  size[Max], chain[Max], father[Max], son[Max];

    public :

        int deep[Max];

        void Dfs_1 (int now, int Father)
        {
            size[now] = 1;
            dfn[now] = ++ Count;
            for (int i = G.list[now]; i; i = G.e[i].next)
                if (G.e[i].to != Father)
                {
                    Dfs_1 (G.e[i].to, now);
                    father[now] = Father;
                    size[now] += size[G.e[i].to];
                    deep[now] = deep[Father] + 1;
                    value[G.e[i].to] = min (G.e[i].w, value[now]);
                    if (size[son[now]] < size[G.e[i].to])
                       son[now] = G.e[i].to;
                }
        }       

        void Dfs_2 (int now, int point)
        {
           chain[now] = point;
           if (son[now])
              Dfs_2 (son[now], point);
           else return ;
           for (int i = G.list[now]; i; i = G.e[i].next)
              if (G.e[i].to != son[now] && G.e[i].to != father[now])
                 Dfs_2 (G.e[i].to, G.e[i].to);
        }

        int Get_Lca (int x, int y)
        {
            for (; chain[x] != chain[y]; )
            {
                if (deep[chain[x]] < deep[chain[y]])
                    swap (x, y);
                x = father[chain[x]];
            }
            return deep[x] < deep[y] ? x : y;
        }

        inline void Insert_edges (const int L)
        {
            for (int i = 1, x, y, z; i <= L; ++ i)
            {
                read (x), read (y), read (z);
                G.Add_Edge (x, y, z);
                G.Add_Edge (y, x, z);
            }
            value[1] = INF;
            deep[1] = 0;
            Dfs_1 (1, 0);
            Dfs_2 (1, 1);
        }
};
Tree_Chain_Get Lca;

inline bool Comp (const int &x, const int &y)
{
    return dfn[x] < dfn[y];
}

class Virtual_Tree
{
    private : Graph T; int Stack[Max], top;
            long long dp[Max];
            int queue[Max];

    public :

        Virtual_Tree () {top = 0;}

        void Build_Tree ()
        {
            int M;
            read (M);
            for (int i = 1; i <= M; ++ i)
                read (queue[i]);
            std :: sort (queue + 1, queue + 1 + M, Comp);
            int cur = 0;
            for (int i = 2; i <= M; ++ i)
                if (Lca.Get_Lca (queue[i], queue[cur]) != queue[cur])
                    queue[++ cur] = queue[i];
            int top = 0;
            Stack[++ top] = 1;
            int __lca;
            T.Clear ();
            for (int i = 1; i <= cur; ++ i)
            {
                __lca = Lca.Get_Lca (Stack[top], queue[i]);
                for (; ; )
                {
                    if (Lca.deep[Stack[top - 1]] <= Lca.deep[__lca])
                    {
                        T.Add_Edge (__lca, Stack[top]);
                        -- top;
                        if (Stack[top] != __lca)
                            Stack[++ top] = __lca;
                        break;
                    }
                    T.Add_Edge (Stack[top - 1], Stack[top]);
                    -- top;
                }
                if (Stack[top] != queue[i])
                    Stack[++ top] = queue[i];
            }
            top --;
            for (; top; -- top)
                T.Add_Edge (Stack[top], Stack[top + 1]);
            Dp (1);
            printf ("%lld\n", dp[1]);
        }

        void Dp (int now)
        {
            long long res = 0; dp[now] = value[now];
            for (int i = T.list[now]; i; i = T.e[i].next)
            {
                Dp (T.e[i].to);
                res += dp[T.e[i].to];
            }
            T.list[now] = 0;
            if (!res)
                dp[now] = value[now];
            else if (res < dp[now])
                dp[now] = res;
        }

        void Doing (const int &K)
        {
            for (int i = 1; i <= K; ++ i)
                Build_Tree ();
        }
};

Virtual_Tree V_T;

int main (int argc, char *argv[])
{
    read (N);

    Lca.Insert_edges (N - 1);
    int K;
    read (K);
    V_T.Doing (K);

    return 0;
}
时间: 2024-10-10 06:31:18

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

●洛谷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

洛谷 P2495 [SDOI2011]消耗战

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

P2495 [SDOI2011]消耗战 虚树

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

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

AC日记——[SDOI2011]消耗战 洛谷 P2495

[SDOI2011]消耗战 思路: 建虚树走树形dp: 代码: #include <bits/stdc++.h> using namespace std; #define INF 1e17 #define maxn 250005 #define ll long long #define maxm (maxn<<1) struct LandType { ll id,key; bool operator<(const LandType pos)const { return key

bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

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

[Sdoi2011]消耗战

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

【BZOJ2286】[Sdoi2011]消耗战 虚树

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

[Luogu 2486] SDOI2011 染色

[Luogu 2486] SDOI2011 染色 树剖水题,线段树维护. 详细题解明天写. 我只想说我写的线段树又变漂亮了qwq #include <algorithm> #include <cstdio> #include <cstring> const int MAXN=100010; int n,m; class HLD { private: bool vis[MAXN]; int num; static int rank[MAXN]; static struct