实际上基环树DP的名字是假的。。

这个限制关系可以看成每个点有一条出边,所以就是一个内向基环树森林。

找出每个基环树的环,然后对于树的部分,做DP,设状态选或不选为$f_{x,0/1}$,则

$f_{x,0}=\sum\limits_{y\in son_x} \max\{f_{y,0},f_{y,1}\}$

$f_{x,1}=\sum\limits_{y\in son_x} \max\{f_{y,0},f_{y,1}\}+[全选了f_{y,1}?]\sum\limits_{y\in son_x} \max\{f_{y,0}-f_{y,1}\}$

这个DP很简单,但是在环上很难处理,因为每个点取不取既和环上前一个点有关也和儿子有关,这个环上的点DP转移是有后效性的。

采用基环树DP另一个常用的手段:断环成树,树形容易处理,避免后效性。断环时,应当注意断开的环上两个点相互影响的关系。

比如此题,每一个点可能会影响后一个点的选择,分两种情况:此点不影响下一个点,则断开后,以此点为根做树形DP,那么两点互不影响,题目条件都成立。

若考虑有影响,则需要这个点不选,让下一个点可以随便选儿子的两个状态较大值而不用担心没有指向他的不选的点。

综上,断边后做两次DP即可。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<cmath>
  6. #include<queue>
  7. #define mst(x) memset(x,0,sizeof x)
  8. #define dbg(x) cerr << #x << " = " << x <<endl
  9. #define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
  10. using namespace std;
  11. typedef long long ll;
  12. typedef double db;
  13. typedef pair<int,int> pii;
  14. template<typename T>inline T _min(T A,T B){return A<B?A:B;}
  15. template<typename T>inline T _max(T A,T B){return A>B?A:B;}
  16. template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
  17. template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
  18. template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
  19. template<typename T>inline T read(T&x){
  20. x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
  21. while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
  22. }
  23. const int N=1e6+;
  24. struct thxorz{
  25. int head[N],nxt[N<<],to[N<<],tot;
  26. thxorz(){tot=;}
  27. inline void add(int x,int y){
  28. to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
  29. to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
  30. }
  31. }G;
  32. int n;
  33. #define y G.to[j]
  34. int vis[N],to,rt,flag,ans,res,ban;
  35. void dfs(int x){//dbg(x);
  36. vis[x]=;
  37. for(register int j=G.head[x];j;j=G.nxt[j])if(!(j&)){
  38. if(vis[y])return rt=x,to=y,ban=j,void();
  39. else dfs(y);
  40. }
  41. }
  42. int f[N][];
  43. void dp1(int x,int fa){
  44. vis[x]=;int chosen=;f[x][]=;
  45. for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa&&j^ban&&j^(ban^)){
  46. dp1(y,x);
  47. if(f[y][]<f[y][])f[x][]+=f[y][],f[x][]+=f[y][];
  48. else f[x][]+=f[y][],f[x][]+=f[y][],chosen=;
  49. }
  50. if(chosen){
  51. int tmp=-N;
  52. for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa&&j^ban&&j^(ban^))MAX(tmp,f[y][]-f[y][]);
  53. f[x][]+=tmp;
  54. }//dbg2(x,chosen);
  55. }
  56. void dp2(int x,int fa){
  57. int chosen=;f[x][]=,f[x][]=;
  58. for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa&&j^ban&&j^(ban^)){
  59. dp2(y,x);
  60. if(f[y][]<f[y][])f[x][]+=f[y][],f[x][]+=f[y][];
  61. else f[x][]+=f[y][],f[x][]+=f[y][],chosen=;
  62. }
  63. if(chosen&&x!=to){
  64. int tmp=-N;
  65. for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa&&j^ban&&j^(ban^))MAX(tmp,f[y][]-f[y][]);
  66. f[x][]+=tmp;
  67. }
  68. }
  69. #undef y
  70.  
  71. int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
  72. read(n);
  73. for(register int i=,x;i<=n;++i)read(x),G.add(i,x);
  74. for(register int i=;i<=n;++i,res=)if(!vis[i]){
  75. dfs(i);
  76. dp1(rt,);//不考虑断环之后根对断点有影响
  77. res=_max(f[rt][],f[rt][]);
  78. dp2(rt,);//考虑断环之后根对断点有影响(即不选根)
  79. MAX(res,f[rt][]);
  80. ans+=res;
  81. }
  82. printf("%d\n",ans);
  83. return ;
  84. }

总结:基环树另一种常见做法:断环成树,考虑影响,做两次树形DP。

BZOJ3037 创世纪[基环树DP]的更多相关文章

  1. tyvj 创世纪 - 基环树

    codevs :   传送门 Description 上帝手中有着N 种被称作“世界元素”的东西,现在他要把它们中的一部分投放到一个新的空间中去以建造世界. 每种世界元素都可以限制另外一种世界元素,所 ...

  2. BZOJ3037 创世纪(基环树DP)

    基环树DP,攻的当受的儿子,f表选,g表不选.并查集维护攻受关系.若有环则记录,DP受的后把它当祖宗,再DP攻的. #include <cstdio> #include <iostr ...

  3. bzoj1791[IOI2008]Island岛屿(基环树+DP)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...

  4. 【bzoj1040】[ZJOI2008]骑士 并查集+基环树dp

    题目描述 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在 ...

  5. BZOJ1040:骑士(基环树DP)

    Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境中 ...

  6. 基环树DP

    基环树DP Page1:问题 啥是基环树?就是在一棵树上增加一条边. Page2:基环树的几种情况 无向 有向:基环外向树,基环内向树. Page3:处理问题的基本方式 1.断环成树 2.分别处理树和 ...

  7. [CSP-S模拟测试]:卡常题/b(基环树+DP)

    题目描述 $ρ$有一个二分连通无向图,$X$方点.$Y$方点均为$n$个(编号为$1\sim n$).这个二分图比较特殊,每一个$Y$方点的度为$2$,一条黑色边,一条白色边.所有黑色边权值均为$a$ ...

  8. [ZJOI2008] 骑士 - 基环树dp

    一类基环树dp都是这个套路吧 随便拆掉环上的一条边 然后跑树形dp,设\(f[i][0/1]\)表示以第\(i\)个人为根的子树,第\(i\)个人选或不选,能收获的最大值 以断点\(u,v\)为根分别 ...

  9. [bzoj2878][Noi2012]迷失游乐园(基环树dp)

    [bzoj2878][Noi2012]迷失游乐园(基环树dp) bzoj luogu 题意:一颗数或是基环树,随机从某个点开始一直走,不走已经到过的点,求无路可走时的路径长期望. 对于一棵树: 用两个 ...

随机推荐

  1. 转换函数conversion function

    类转换分为两个角度 转换自身为其他类型 把其他类型转换为自身 Example: 这里我们可以将b转换为class xxx 的类型(方式2),也可以将me转换为double,然后再讲结果转换为doubl ...

  2. eNSP——ARP及ProxyARP

    原理: ARP (Address Resolution Protocol)是用来将IP地址解析为MAC地址的协议.ARP表项可以分为动态和静态两种类型.动态ARP是利用ARP广播报文,动态执行并自动进 ...

  3. 关于Angular+ngx-perfect-scrollbar自定义各大浏览器滚动条样式的解决方法

    资料: http://manos.malihu.gr/jquery-custom-content-scroller/  (此项是结合Jquery使用的,在此并未采用) https://www.npmj ...

  4. [转帖]SPARC简介

    https://www.cnblogs.com/chaohm/p/5674886.html 1.    概述 SPARC(Scalable Processor ARChitecture,可扩展处理器架 ...

  5. Thinkphp命令行快速生成模型类方法

    进去cmd,切换到项目根目录,也就是think文件所在目录,执行下面的指令可以生成index模块的blog模型类文件: >php think make:model index/Blog 生成的模 ...

  6. java的设计模式的一些链接,站在巨人的肩膀上,才能看的更远。(均来源与网上的各个大牛的博客中)

    创建型抽象工厂模式 http://www.cnblogs.com/java-my-life/archive/2012/03/28/2418836.html工厂方法 http://www.cnblogs ...

  7. Python脚本:Linux自动化执行Python脚本

    1.环境及其工具: ubuntu 16.04 python2.7(自带) pip2.7(安装) virtualenv(安装) crontab (自带) 2.pip2.7安装 (1)尝试使用 sudo ...

  8. NopCommerce的autofac的理解

    nop项目4.1是core.2.1开发的,Startup.cs文件开始 从入口进去ServiceCollectionExtensions这个文件 this IServiceCollection ser ...

  9. STM32-移植FATFS的NANDFLASH驱动

    一,建立工程FATFS源码 1,在http://elm-chan.org/fsw/ff/00index_e.html上下载ff007c.zip,并把ff007c.zip里面的 src文件夹复制到D:\ ...

  10. boost交叉编译

    运行bootstrap.sh # ./bootstrap.sh 生成b2.bjam和project-config.jam文件 修改project-config.jam using gcc : arm ...