P2495 [SDOI2011]消耗战 虚树

Posted dukelv

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
",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
",ft :: pr(1));
    }
    return 0;
}

 

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

luogu P2495 [SDOI2011]消耗战

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

P2495 [SDOI2011]消耗战

●洛谷P2495 [SDOI2011]消耗战

Luogu P2495 [SDOI2011]消耗战

题解SDOI2011消耗战