zoukankan      html  css  js  c++  java
  • P2495 [SDOI2011]消耗战 lca倍增+虚树+树形dp

    题目:给出n个点的树  q次询问  问切断 k个点(不和1号点联通)的最小代价是多少

    思路:树形dp  sum[i]表示切断i的子树中需要切断的点的最小代价是多少 mi[i]表示1--i中的最小边权

    sum[i]=min(mi[i],sigma(min(mi[v],sum[v]) (v∈i.son))

    从根向上dp  这里巧妙运用了欧拉序(每个点入和出的按时间顺序排列的序列)

    题目链接:https://www.luogu.org/problemnew/show/P2495

    参考:https://www.luogu.org/problemnew/solution/P2495

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 const int maxn = 250007;
      4 typedef long long ll;
      5 const int N=maxn;
      6 struct data{
      7     int v;int nxt;ll val;
      8 }edge[2*maxn];
      9 int dfu;
     10 int dfin[N];//欧拉序入的时间戳
     11 int dfou[N];//欧拉序列出的时间戳
     12 int fa[22][N];//倍增用
     13 ll mi[N];//i到1号点路径中最小的边权
     14 int dep[N]; //深度
     15 int lca(int u,int v){//倍增lca
     16     if(dep[u]<dep[v])swap(u,v);
     17     int del=dep[u]-dep[v];
     18     for(int i=0;del;del>>=1,i++){
     19         if(del&1){
     20             u=fa[i][u];
     21         }
     22     }//到同一深度
     23     if(u==v)return u;
     24     for(int i=20;i>=0;i--){
     25         if(fa[i][u]!=fa[i][v]){u=fa[i][u],v=fa[i][v];}
     26     } //从到lca的距离的二进制理解 即可立即为什么fa[0][v]就是是lca
     27     return fa[0][v];
     28 }
     29 int alist[maxn],cnt;int n;
     30 
     31 void dfs(int x){
     32     dfin[x]=++dfu;//首位是入 出 时间戳
     33     for(int i=1;fa[i-1][x];i++){//更新父节点信息
     34         fa[i][x]=fa[i-1][fa[i-1][x]];
     35     }
     36     int nxt=alist[x];
     37     while(nxt){//更新最小边权
     38         int v=edge[nxt].v,val=edge[nxt].val;
     39         if(dfin[v]==0){
     40             dep[v]=dep[x]+1;
     41             mi[v]=min(mi[x],1ll*val);
     42             fa[0][v]=x;
     43             dfs(v);
     44         }
     45         nxt=edge[nxt].nxt;
     46     }
     47     dfou[x]=++dfu;
     48     return ;
     49 }
     50 inline void add(int u,int v,ll val)
     51 {    edge[++cnt].v=v;
     52     edge[cnt].nxt=alist[u];
     53     alist[u]=cnt;
     54     edge[cnt].val=val;
     55 }
     56 bool cmp(int x,int y){//分为正负即可方便得判断是入 还是出
     57     int k1=(x>0)?dfin[x]:dfou[-x];
     58     int k2=(y>0)?dfin[y]:dfou[-y];
     59     return k1<k2;
     60 }
     61 int tr[4*N];
     62 stack<int>s;
     63 int m;
     64 bool book[N];
     65 ll sum[N];
     66 
     67 int main(){
     68     scanf("%d",&n);
     69     for(int i=1;i<=n-1;i++){
     70         int x,y,z;
     71         scanf("%d%d%d",&x,&y,&z);
     72         add(x,y,z);add(y,x,z);
     73     }
     74     mi[1]=0x3f3f3f3f;
     75     dfs(1);//建立欧拉序
     76     scanf("%d",&m);
     77     for(int i=1;i<=m;i++){
     78         int tmp;
     79         scanf("%d",&tmp);
     80         for(int j=1;j<=tmp;j++){//把要求的点标记
     81             scanf("%d",&tr[j]);
     82             book[tr[j]]=1;
     83             sum[tr[j]]=mi[tr[j]];//初始化删除重要点的sum[i](sum指的是切断[i]的子树中所有重要点的最小代价 初始化为切掉该点即 从1--i的最小边权 意思是直接把这个子树切掉了)
     84         }
     85         sort(tr+1,tr+tmp+1,cmp);//对欧拉序列排序建立虚树
     86         for(int j=1;j<tmp;j++){
     87             int lc=lca(tr[j],tr[j+1]);
     88             if(!book[lc]){//树的建立
     89                 tr[++tmp]=lc;
     90                 book[lc]=1;
     91             }
     92         }
     93         int nc=tmp;
     94         for(int j=1;j<=nc;j++){//把每个点的负时间戳也加入 用负数表示这个的点现在是出去的点
     95             tr[++tmp]=-tr[j];
     96         }
     97         if(!book[1]){//强行把1号点加进来当根
     98             tr[++tmp]=1;tr[++tmp]=-1;
     99         }
    100         sort(tr+1,tr+tmp+1,cmp);//重构欧拉序
    101         for(int j=1;j<=tmp;j++){//模拟dfs 因为1--tmp是欧拉序列 所以可以直接用
    102             if(tr[j]>0)s.push(tr[j]);//进栈
    103             else {
    104                 int now=s.top();s.pop();
    105                 if(now!=1){
    106             //状态转移方程sum[i]=sigma(min(mi[v],sum[v]) (v∈i.son)
    107                     int fa=s.top();
    108                     sum[fa]+=min(sum[now],mi[now]);
    109                 }    
    110                 else printf("%lld
    ",sum[1]);
    111                 sum[now]=0;
    112                 book[now]=false;
    113             }
    114         }
    115     }
    116 
    117     return 0;
    118 }
    View Code
  • 相关阅读:
    我的利器
    老树新芽——矩估计遇到神经网络
    QuantLib 金融计算——数学工具之随机数发生器
    《Implementing QuantLib》译后记
    c# 大批量用户访问数据库报错
    阿里云redis映射到阿里云服务器
    C# 输出字符串到文本文件中
    SQL Server 怎么在分页获取数据的同时获取到总记录数
    C# 当前 .NET SDK 不支持将 .NET Core 2.1 设置为目标。请将 .NET Core 2.0 或更低版本设置为目标,或使用支持 .NET Core 2.1 的 .NET SDK 版本。
    c# 项目之间循环引用vs弹窗提醒
  • 原文地址:https://www.cnblogs.com/ttttttttrx/p/10632774.html
Copyright © 2011-2022 走看看