实际上基环树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即可。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define mst(x) memset(x,0,sizeof x)
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=1e6+;
struct thxorz{
int head[N],nxt[N<<],to[N<<],tot;
thxorz(){tot=;}
inline void add(int x,int y){
to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
}
}G;
int n;
#define y G.to[j]
int vis[N],to,rt,flag,ans,res,ban;
void dfs(int x){//dbg(x);
vis[x]=;
for(register int j=G.head[x];j;j=G.nxt[j])if(!(j&)){
if(vis[y])return rt=x,to=y,ban=j,void();
else dfs(y);
}
}
int f[N][];
void dp1(int x,int fa){
vis[x]=;int chosen=;f[x][]=;
for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa&&j^ban&&j^(ban^)){
dp1(y,x);
if(f[y][]<f[y][])f[x][]+=f[y][],f[x][]+=f[y][];
else f[x][]+=f[y][],f[x][]+=f[y][],chosen=;
}
if(chosen){
int tmp=-N;
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][]);
f[x][]+=tmp;
}//dbg2(x,chosen);
}
void dp2(int x,int fa){
int chosen=;f[x][]=,f[x][]=;
for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa&&j^ban&&j^(ban^)){
dp2(y,x);
if(f[y][]<f[y][])f[x][]+=f[y][],f[x][]+=f[y][];
else f[x][]+=f[y][],f[x][]+=f[y][],chosen=;
}
if(chosen&&x!=to){
int tmp=-N;
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][]);
f[x][]+=tmp;
}
}
#undef y int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
read(n);
for(register int i=,x;i<=n;++i)read(x),G.add(i,x);
for(register int i=;i<=n;++i,res=)if(!vis[i]){
dfs(i);
dp1(rt,);//不考虑断环之后根对断点有影响
res=_max(f[rt][],f[rt][]);
dp2(rt,);//考虑断环之后根对断点有影响(即不选根)
MAX(res,f[rt][]);
ans+=res;
}
printf("%d\n",ans);
return ;
}

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

最新文章

  1. 2015百度之星1002 查找有序序列(RMQ+主席树模板水过)
  2. 彻底理解数字图像处理中的卷积-以Sobel算子为例
  3. Linux安装oracle 10g常见问题之——OUI-25031
  4. react-redux原理
  5. seo初学
  6. 64位Ubuntu14.04搭建ADT开发环境
  7. iOS基础 - iOS程序启动原理
  8. “百度杯”CTF比赛 九月场_123(文件备份,爆破,上传)
  9. js判断输入的input内容是否为数字
  10. whil
  11. git操作手册
  12. Creator 插件商店:高品质插件
  13. Java技术开发中的坑
  14. iostat使用
  15. Docker:unauthorized: incorrect username or password.
  16. TIMER_PWM_CAPTURE
  17. angular2 文件上传
  18. 装有多个版本 office,选择默认的版本 打开文件
  19. Leetcode 之Same Tree(48)
  20. Vmware虚拟机三种网络模式详解(转)

热门文章

  1. 大周末的不休息,继续学习pandas吧,pandas你该这么学,No.7
  2. Ajax的使用及后台如何传参
  3. spark 执行报错 java.io.EOFException: Premature EOF from inputStream
  4. 剑指offer24:二叉树中和为输入整数值的所有路径。(注意: 在返回值的list中,数组长度大的数组靠前)
  5. PostgreSQL-存储过程
  6. Sql Server 收缩日志文件原理及always on 下的实践
  7. WebApi 空项目生成帮助文档
  8. PHP如何通过URL访问,获得新的URL 两种方法
  9. 查找最大和次大元素(JAVA版)(分治法)
  10. html中正则匹配img