zoukankan      html  css  js  c++  java
  • P2495 [SDOI2011]消耗战 (虚树+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
    

    说明/提示

    【数据规模和约定】

    对于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

    SOLUTION:

    对于每次询问的关键点,建立虚数,两个点之间的边权为在原树上两点之间路径的最下值

    dp的时候,当这个点是关键点,则加上边权

    若不是,则加上 min (边权 ,断开这儿子子树内所有关键点的花费)

    CODE:

    #include"iostream"
    #include"algorithm"
    #include"vector"
    #include"stdio.h"
    using namespace std;
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    
    const int N = 250007;
    
    
    vector<int> RG[N],VG[N];
    int U[N],V[N],C[N];
    int dfn[N],deep[N];
    ll me[N];
    int fa[N][20];
    int stk[N],top;
    int idq[N],mark[N];
    int n,m,idx;
    int f[N][20];
    int dp[N];
    struct aa
    {
        int so;
        int w;
    };
    vector<aa> G[N];
    
    int LCA(int u,int v)
    {
        if(deep[u] < deep[v]) swap(u,v);
        int delta = deep[u] - deep[v];
        for(int i = 19; i >= 0; --i)
        {
            if((delta >> i) & 1) u = fa[u][i];
        }
        for(int i = 19; i >= 0; --i)
        {
            if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
        }
        if(u == v) return u;
        return fa[u][0];
    }
    
    int DIS(int u,int v)
    {
        if(deep[u] < deep[v]) swap(u,v);
        int delta = deep[u] - deep[v];
        int ans=1e16;
        for(int i = 19; i >= 0; --i)
        {
            if((delta >> i) & 1) ans=min(ans,f[u][i]),u = fa[u][i];
        }
        if(u == v) return ans;
        for(int i = 19; i >= 0; --i)
        {
            if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
        }
    
        return fa[u][0];
    }
    
    void insert2(int u)
    {
        if(top == 1)
        {
            stk[++top] = u;
            return;
        }
        int lca = LCA(u,stk[top]);
        if(lca == stk[top])
        {
            stk[++top] = u;
            return ;
        }
        while(top > 1 && dfn[lca] <= dfn[stk[top-1]])
        {
            G[stk[top-1]].push_back({stk[top],DIS(stk[top],stk[top-1])});
            --top;
        }
        if(lca != stk[top])
        {
            G[lca].push_back({stk[top],DIS(stk[top],lca)});
            stk[top] = lca;
        }
        stk[++top] = u;
    }
    
    ll dp2(int x)
    {
        ll cost=0;
        int len=G[x].size();
        for(int j=0;j<len;j++)
        {
            aa i=G[x][j];
            if(mark[i.so])cost+=i.w;
            else cost+=min(1ll*i.w,dp2(i.so));
        }
        return cost;
    
    }
    void Clear(int x)
    {
        int len=G[x].size();
         for(int j=0;j<len;j++)
        {
            aa i=G[x][j];
            Clear(i.so);
    
        }
        G[x].clear();
    }
    
    /***********************************/
    
    
    void dfs(int u)
    {
        dfn[u] = ++idx;
        deep[u] = deep[fa[u][0]] + 1;
        int len=RG[u].size();
        for(int j=0;j<len;j++)
        {
            int e=RG[u][j];
            int v = U[e] ^ V[e] ^ u;
            if(v == fa[u][0]) continue;
            me[v] = C[e];
            f[v][0]=C[e];
            if(u != 1 && me[u] < me[v]) me[v] = me[u];
            fa[v][0] = u;
            dfs(v);
        }
    }
    
    
    bool comp(int a,int b)
    {
        return dfn[a] < dfn[b];
    }
    /***********************************/
    
    #define sc(x) scanf("%d",&(x))
    
    signed main()
    {
        cin >> n;
        for(int i = 1; i < n; ++i)
        {
            sc(U[i]); sc(V[i]); sc(C[i]);
            RG[U[i]].push_back(i);
            RG[V[i]].push_back(i);
        }
        dfs(1);
    
        for(int t = 1; t <= 19; ++t) for(int i = 1; i <= n; ++i)
            {
                fa[i][t] = fa[fa[i][t-1]][t-1];
                if(fa[i][t])  f[i][t] = min(f[i][t-1] , f[fa[i][t-1]][t-1]);
            }
    
        cin >> m;
        for(int i = 0; i < m; ++i)
        {
            int sz;
            sc(sz);
            for(int j = 0; j < sz; ++j)
            {
                sc(idq[j]);
                mark[idq[j]] = 1;
            }
            sort(idq,idq+sz,comp);
            top = 0;
            stk[++top] = 1;
            for(int j = 0; j < sz; ++j) insert2(idq[j]);
            while(top > 0)
            {
                G[stk[top-1] ].push_back({stk[top],DIS(stk[top-1],stk[top])});
                top--;
            }
            printf("%lld
    ",dp2(1));
            Clear(1);
            for(int j = 0; j < sz; ++j) mark[idq[j]] = 0;
    
        }
        return 0;
    }
    

      

  • 相关阅读:
    9. 如何在控制器或模型中获取当前登录的管理员或登录用户信息
    FastAdmin控制管理员只显示自己添加的数据
    div 隐藏
    使用JavaScript实现Input输入数据后自动计算并实时显示
    thinkphp中在页面怎么格式输出小数和时间
    fastadmin表单提交提示红色OK
    在某个域被使用或改变时,它会改变颜色。【用于提示表单已填充】
    Unix命令行学习
    Ubuntu菜鸟入门(十)—— Flash控件安装
    Ubuntu菜鸟入门(九)—— 支付宝支付控件安装
  • 原文地址:https://www.cnblogs.com/zhangbuang/p/11310467.html
Copyright © 2011-2022 走看看