zoukankan      html  css  js  c++  java
  • bzoj 1040 [ZJOI2008]骑士(基环外向树,树形DP)

    【题目链接】

        http://www.lydsy.com/JudgeOnline/problem.php?id=1040

       

    【题意】

        给一个基环森林,每个点有一个权值,求一个点集使得点集中的点无边相连且权值和最大。

    【思路】

        注意题目中的有向边其实就是无向边。然后有多个联通块,每个联通块中有且仅有一个环。

        如果没有环的话可以用树形DP,解决这个问题。

        设f[i][0],f[i][1]分别表示以i为根,不选/选i时的最大权值。则有转移式:

            f[i][0]=sigma{ max(f[son(i)][0],f[son(i)][1]) }

            f[i][1]=sigma{ f[son(i)][0] }

        对于一个环,我们任选一条边拆开,然后以边的两点U,V为根做树形DP,再考虑边UV存在,有两种情况:

      1) 强制不选U,V任意,环的贡献为以U做DP的f[U][0]

      2) 强制不选V,U任意,环的贡献为以V做DP的f[V][0]

    【科普】

        基环外向树就是一棵树加一条边(好厉害的名字<_<

    【代码】

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
     5 using namespace std;
     6 
     7 typedef long long ll;
     8 const int N = 1e6+10;
     9 
    10 struct Edge {
    11     int v,nxt;
    12 }e[N<<1];
    13 int en=1,front[N];
    14 void adde(int u,int v)
    15 {
    16     en++; e[en].v=v,e[en].nxt=front[u],front[u]=en;
    17 }
    18 
    19 int n,w[N],vis[N];
    20 ll f[N][2];
    21 
    22 ll read()
    23 {
    24     char c=getchar(); ll f=1,x=0;
    25     while(!isdigit(c)) {if(c=='-') f=-1; c=getchar(); };
    26     while(isdigit(c)) x=x*10+c-'0',c=getchar();
    27     return x*f;    
    28 }
    29 
    30 int U,V,E;
    31 void dfs(int u,int fa) 
    32 {
    33     vis[u]=1;
    34     for(int i=front[u];i;i=e[i].nxt) {
    35         if((i^1)==fa) continue;
    36         int v=e[i].v;
    37         if(vis[v]) {
    38             U=u; V=v; E=i;
    39             continue;
    40         }
    41         dfs(v,i);
    42     }
    43 }
    44 void treedp(int u,int fa,int ban)
    45 {
    46     f[u][1]=w[u],f[u][0]=0;
    47     for(int i=front[u];i;i=e[i].nxt) {
    48         if((i^1)==fa) continue;
    49         if(i==ban||(i^1)==ban) continue;
    50         int v=e[i].v;
    51         treedp(v,i,ban);
    52         f[u][0]+=max(f[v][1],f[v][0]);
    53         f[u][1]+=f[v][0];
    54     }
    55 }
    56 
    57 int main()
    58 {
    59     n=read();
    60     int v;
    61     FOR(i,1,n) {
    62         w[i]=read(),v=read();
    63         adde(i,v),adde(v,i);
    64     }
    65     ll ans=0;
    66     FOR(i,1,n) if(!vis[i]) {
    67         dfs(i,-1);
    68         treedp(U,-1,E);
    69         ll tmp=f[U][0];
    70         treedp(V,-1,E);
    71         tmp=max(tmp,f[V][0]);
    72         ans+=tmp;
    73     }
    74     printf("%lld",ans);
    75     return 0;
    76 }
  • 相关阅读:
    数仓备机DN重建:快速修复你的数仓DN单点故障
    深度学习分类任务常用评估指标
    云小课 | MRS基础入门之HDFS组件介绍
    华为云数据库GaussDB(for Cassandra)揭秘第二期:内存异常增长的排查经历
    为什么vacuum后表还是继续膨胀?
    Go 自定义日志库
    Go time包
    Go 文件操作
    Go 包相关
    【程序人生】跟小伙伴们聊聊我有趣的大学生活和我那两个好基友!
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5275131.html
Copyright © 2011-2022 走看看