• 消耗战——dp+虚树


    题目

    【题目描述】

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

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


    【输入格式】

    第一行一个整数 $n$ ,代表岛屿数量。

    接下来 $n-1$ 行,每行三个整数 $u,v,w$ ,代表 $u$ 号岛屿和 $v$ 号岛屿由一条代价为 $c$ 的桥梁直接相连,保证 $1 le u,v le n$ 且 $1 le c le 100000 $ 。

    第 $n+1$ 行,一个整数 $m$ ,代表敌方机器能使用的次数。

    接下来 $m$ 行,每行一个整数 $k_i$ ,代表第 $i$ 次后,有 $k_i$ 个岛屿资源丰富,接下来 $k$ 个整数$h_1,h_2,…h_k$ ,表示资源丰富岛屿的编号。


    【输出格式】
    输出有 $m$ 行,分别代表每次任务的最小代价。
    【样例输入】

    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


    【样例输出】

    12
    32
    22


    【数据范围与提示】
    对于100%的数据, $2 le n le 250000,m ge 1, sum{k_i} le 500000,1 le k_i le n-1 $

    题解

    考虑树形 dp,记 $ g[i] $ 表示将 $ i $ 及其子树断开的最小代价,然后发现效率过不去

    因为 $ sum k leq 5 imes 10^5 $,考虑构造虚树,然后直接 dp 即可

    时间效率:$ O(2log n imes sum k) $,因为一棵虚树最多只有 $ 2k $ 个点,查询 LCA 的效率为 $ O(log n) $

    构造虚树:https://www.cnblogs.com/zwfymqz/p/9175152.html

    代码

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define pb push_back
     4 #define _(d) while(d(isdigit(ch=getchar())))
     5 using namespace std;
     6 int R(){
     7     int x;bool f=1;char ch;_(!)if(ch=='-')f=0;x=ch^48;
     8     _()x=(x<<3)+(x<<1)+(ch^48);return f?x:-x;}
     9 const int N=2.5e5+5;
    10 int n,m,f[N][25],head[N],cnt,dfn[N],dep[N],a[N],sta[N],top,tot,lg;
    11 LL g[N];
    12 vector<int>G[N];
    13 struct edge{int to,nex;LL w;}e[N<<1];
    14 bool cmp(int a,int b){return dfn[a]<dfn[b];}
    15 void add(int s,int t,int w){e[++cnt]=(edge){t,head[s],w},head[s]=cnt;}
    16 void dfs(int u,int far){
    17     dfn[u]=++tot,f[u][0]=far,dep[u]=dep[far]+1;
    18     for(int i=1;i<=lg;i++)f[u][i]=f[f[u][i-1]][i-1];
    19     for(int k=head[u],v;k;k=e[k].nex)
    20         if((v=e[k].to)!=far)
    21             g[v]=min(g[u],e[k].w),dfs(v,u);
    22 }
    23 int lca(int x,int y){
    24     if(dep[x]>dep[y])swap(x,y);
    25     int i=lg;
    26     while(dep[x]<dep[y]){
    27         while(i&&dep[x]>dep[f[y][i]])i--;
    28         y=f[y][i];}
    29     i=lg;
    30     while(x!=y){
    31         while(i>0&&f[x][i]==f[y][i])i--;
    32         x=f[x][i],y=f[y][i];}
    33     return x;
    34 }
    35 void insert(int x){
    36     if(top==1){sta[++top]=x;return;}
    37     int far=lca(x,sta[top]);
    38     if(far==sta[top])return;
    39     while(top>1&&dfn[sta[top-1]]>=dfn[far])
    40         G[sta[top-1]].pb(sta[top]),top--;
    41     if(far!=sta[top])G[far].pb(sta[top]),sta[top]=far;
    42     sta[++top]=x;
    43     return;
    44 }
    45 LL work(int x){
    46     if(!G[x].size())return g[x];
    47     LL sum=0;
    48     for(int i=G[x].size()-1;~i;i--)
    49         sum+=work(G[x][i]);
    50     G[x].clear();
    51     return min(sum,(LL)g[x]);
    52 }
    53 int main(){
    54     memset(g,0x3f,sizeof g);
    55     n=R(),lg=log2(n)+1;
    56     for(int i=1,u,v,w;i<n;i++)
    57         u=R(),v=R(),w=R(),add(u,v,w),add(v,u,w);
    58     dfs(1,1);
    59     for(int q=R();q--;){
    60         m=R();
    61         for(int i=1;i<=m;i++)a[i]=R();
    62         sort(a+1,a+1+m,cmp);
    63         sta[top=1]=1;
    64         for(int i=1;i<=m;i++)insert(a[i]);
    65         while(top)G[sta[top-1]].pb(sta[top]),top--;
    66         printf("%lld
    ",work(1));
    67     }
    68     return 0;
    69 }
    View Code
  • 相关阅读:
    如何以nobody用户执行命令?
    记一次全站代理切换----血的教训
    tomcat十大安全优化措施
    paramiko模块使用
    日志分析 第七章 安装grafana
    日志分析 第六章 安装elasticsearch
    日志分析 第五章 安装logstash
    日志分析 第四章 安装filebeat
    IO多路复用及ThreadingTCPServer源码阅读
    socket编程--socket模块介绍
  • 原文地址:https://www.cnblogs.com/chmwt/p/10661621.html
Copyright © 2020-2023  润新知