- 顾名思义,即树上的
dp
问题。 - 有两种转移方式:一是从根转移到叶子,二是从叶子转移到根。在实际应用中,从叶子转移到根的方式应用的更多。
- 树上
dp
在实现起来的时候,可能需要配合贪心等其它高效的算法,甚至于线段树
等高效数据结构。 - 代码实现起来的时候,常常用
递归
实现,因为树的定义就是递归定义的。同时,一定要注意不要从一个点又转移回了它的父亲,否则,一定会TLE
。 - 因为树的点数
经常是
级别的,所以一定要注意
long long
。
- 基环树是一种特殊的图的结构。它由 个点和 条边组成,不仅全图强联通,而且只有一个环。
- 根据树的特点和基环树的定义,我们可以发现基环树就是一棵树在加一条边。这是基环树很重要的一个性质。
- 所以,一般解决基环树上问题时,我们需要找到环上一边,如何把它断掉,转化为树来求解。
首先,像普通的树一样,从根到叶子访问,访问时记录每个点的父亲。
假设我们正在枚举点 ,枚举与u所有的相邻的点 ,如果 已访问且 不是 的父亲,那么 就是环上的一边。
注意,这种方法只能找一条,且时间复杂度为 。
我们可以随便指定一个顶点 ,从它开始,如果一个点 的 没有被标记,我们先标记 ,如果将 记为 。
不断重复如上的算法,直到某个点 的 已经被标记,那么 就是环上的一边。
Z国
的骑士团
是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。
最近发生了一件可怕的事情,邪恶的Y国
发动了一场针对Z国
的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国
又怎能抵挡的住Y国
的军队。于是人们把所有的希望都寄托在了骑士团
的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。
骑士团
是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。
战火绵延,人民生灵涂炭,组织起一个骑士军团
加入战斗刻不容缓!国王
交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团
,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团
最具有战斗力。
为了描述战斗力,我们将骑士按照 至 编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。
我们把每个骑士和他最痛恨的骑士之间连一条边,并把他最痛恨的骑士作为他的“父亲”。不难发现,这个模型是一棵有向基环树。
所以,我们枚举环上的边,把它断开,转化为树。用类似没有上司的舞会
的
方法就可以完成。
注意数组别开太多,否则任意 。注意常数不要太大,否则任意 。
const int N=1e6+100;
#define ll long long
struct node{
int next,to;
}e[N];int h[N],tot,n,fa[N];
inline void add(int a,int b){
e[++tot]=(node){h[a],b};h[a]=tot;
}
int profit[N];ll f[N][2],ans;bool visit[N];
void dp(int u,int fa,int root){
f[u][1]=profit[u];visit[u]=true;
for(int i=h[u];i;i=e[i].next){
register int to=e[i].to;
if (to==root) continue;
dp(to,u,root);f[u][1]+=f[to][0];
f[u][0]+=max(f[to][0],f[to][1]);
}
}
inline ll calc(int u){
memset(f,0,sizeof(f));
dp(u,-1,u);return f[u][0];
}
void find_circle(int u){
visit[u]=true;int root=u;
while (!visit[fa[root]]){
visit[fa[root]]=true;
root=fa[root];
}
ans+=max(calc(root),calc(fa[root]));
}
inline void initialization(){
memset(visit,0,sizeof(visit));
memset(profit,0,sizeof(profit));
memset(h,0,sizeof(h));ans=tot=0;
}
int main(){
// freopen("t1.in","r",stdin);
n=read();initialization();
for(int i=1;i<=n;i++){
profit[i]=read();
add(fa[i]=read(),i);
}
for(int i=1;i<=n;i++)
if (!visit[i]) find_circle(i);
printf("%lld",ans);
return 0;
}