进阶换根dp
战略威慑(智旭生加强版)伪题解
\(\qquad\!\!\)一句话题意:在 \(O(N)\) 的时间复杂度内求出一棵树删去一条边以后分裂成的两颗新树的直径的乘积的最大值。
\(\qquad\!\!\)相信暴力大家都会,下面就直接说一种比较麻烦的正解吧:
应用算法:换根\(dp\)
我就认为大家对于思想都理解了,我主要讲一下比较复杂的分类讨论和代码实现:
\(\qquad\!\!\)首先,我们考虑一下每个节点需要存储的数据:
\(\qquad\!\!\)由于在 \(dp\) 找直径,所以需要存一下经过每个节点向下的最长链 \(f1\) 和次长链 \(f2\);由于新树的直径不一定过切割点,所以需要存一下每个节点的最长子树路径 \(dpf1\) ,注意这个路径不能包含该点;由于进行 换根\(dp\),需要将旧的根的信息传递给新的根,所以需要再存一个再次长链 \(f3\) 和次长子树路径 \(dpf2\) ;由于进行 换根\(dp\),需要确定新的根对于旧的根的信息继承,所以需要再存一下最长链的最优子节点编号 \(g1\) 和次长链的次优子节点编号 \(g2\) 以及最长子树路径的最优子节点编号 \(dpg1\)。
1 | struct node |
\(\qquad\!\!\)其次,我们考虑初始化每个点的以上变量:
\(\qquad\!\!\)很简单的树形 \(dp\),我们利用一次 \(dfs\) 进行处理,具体内容可自行理解。
\(\qquad\!\!\)这不会还有人不会吧?不会吧。不会吧。
1 | in void dfs1(re int u,re int fa) |
\(\qquad\!\!\)再次,我们考虑进行 换根\(dp\):
\(\qquad\!\!\)我们利用一次 \(dfs\) 进行处理。先考虑统计答案,我们只需要根据 \(dfs\) 的顺序对于每条边不记的情况统计一次答案即可;再考虑换根的过程,我们只需要根据 \(dfs\) 的顺序将每条边的两侧的节点中的父节点(此时的根节点)的信息传递给子节点(新的根节点)即可;最后考虑一下正确性,由于换根的过程只改变新的根一个节点的信息,且回溯后此信息无用,易证该过程是正确的。
\(\qquad\!\!\)由于新的根节点与父节点存储的用于统计答案的数据存在联系,所以就需要进行分类讨论,先看一下程序的框架:
1 | in void dfs2(re int u,re int fa) |
\(\qquad\!\!\)下面进行分类讨论:
一、新的根 \(v\) 为父节点 \(u\) 的最长链的最优子节点 \(g1\):
- 情况一:新的根 \(v\) 为父节点 \(u\) 的最长子树路径的最优子节点 \(dpg1\):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//统计答案:
ans=max(ans,max(max((a[u].f2+a[u].f3)*a[v].dpf1,a[u].dpf2*a[v].dpf1),max((a[u].f2+a[u].f3)*(a[v].f1+a[v].f2),a[u].dpf2*(a[v].f1+a[v].f2))));
//对于子树路径换根:
if(max(a[u].dpf2,a[u].f2+a[u].f3)>a[v].dpf1)
{
a[v].dpf2=a[v].dpf1;
// a[v].dpg2=a[v].dpg1;
a[v].dpf1=max(a[u].dpf2,a[u].f2+a[u].f3);
a[v].dpg1=u;
}
else if(max(a[u].dpf2,a[u].f2+a[u].f3)<=a[v].dpf1&&max(a[u].dpf2,a[u].f2+a[u].f3)>a[v].dpf2)
{
a[v].dpf2=max(a[u].dpf2,a[u].f2+a[u].f3);
// a[v].dpg2=u;
}- 情况二:新的根 \(v\) 不为父节点 \(u\) 的最长子树路径的最优子节点 \(dpg1\):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//统计答案:
ans=max(ans,max(max((a[u].f2+a[u].f3)*a[v].dpf1,a[u].dpf1*a[v].dpf1),max((a[u].f2+a[u].f3)*(a[v].f1+a[v].f2),a[u].dpf1*(a[v].f1+a[v].f2))));
//对于子树路径换根:
if(max(a[u].dpf1,a[u].f2+a[u].f3)>a[v].dpf1)
{
a[v].dpf2=a[v].dpf1;
// a[v].dpg2=a[v].dpg1;
a[v].dpf1=max(a[u].dpf1,a[u].f2+a[u].f3);
a[v].dpg1=u;
}
else if(max(a[u].dpf1,a[u].f2+a[u].f3)<=a[v].dpf1&&max(a[u].dpf1,a[u].f2+a[u].f3)>a[v].dpf2)
{
a[v].dpf2=max(a[u].dpf1,a[u].f2+a[u].f3);
// a[v].dpg2=u;
}- 情况一+情况二:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//统一的对于链换根:
if(a[u].f2+1>a[v].f1)
{
a[v].f3=a[v].f2;
// a[v].g3=a[v].g2;
a[v].f2=a[v].f1;
a[v].g2=a[v].g1;
a[v].f1=a[u].f2+1;
a[v].g1=u;
}
else if(a[u].f2+1<=a[v].f1&&a[u].f2+1>a[v].f2)
{
a[v].f3=a[v].f2;
// a[v].g3=a[v].g2;
a[v].f2=a[u].f2+1;
a[v].g2=u;
}
else if(a[u].f2+1<=a[v].f2&&a[u].f2+1>a[v].f3)
{
a[v].f3=a[u].f2+1;
// a[v].g3=u;
}二、新的根 \(v\) 为父节点 \(u\) 的次长链的次优子节点 \(g2\):
- 情况三:新的根 \(v\) 为父节点 \(u\) 的最长子树路径的最优子节点 \(dpg1\):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//统计答案:
ans=max(ans,max(max((a[u].f1+a[u].f3)*a[v].dpf1,a[u].dpf2*a[v].dpf1),max((a[u].f1+a[u].f3)*(a[v].f1+a[v].f2),a[u].dpf2*(a[v].f1+a[v].f2))));
//对于子树路径换根:
if(max(a[u].dpf2,a[u].f1+a[u].f3)>a[v].dpf1)
{
a[v].dpf2=a[v].dpf1;
// a[v].dpg2=a[v].dpg1;
a[v].dpf1=max(a[u].dpf2,a[u].f1+a[u].f3);
a[v].dpg1=u;
}
else if(max(a[u].dpf2,a[u].f1+a[u].f3)<=a[v].dpf1&&max(a[u].dpf2,a[u].f1+a[u].f3)>a[v].dpf2)
{
a[v].dpf2=max(a[u].dpf2,a[u].f1+a[u].f3);
// a[v].dpg2=u;
}- 情况四:新的根 \(v\) 不为父节点 \(u\) 的最长子树路径的最优子节点 \(dpg1\):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//统计答案:
ans=max(ans,max(max((a[u].f1+a[u].f3)*a[v].dpf1,a[u].dpf1*a[v].dpf1),max((a[u].f1+a[u].f3)*(a[v].f1+a[v].f2),a[u].dpf1*(a[v].f1+a[v].f2))));
//对于子树路径换根:
if(max(a[u].dpf1,a[u].f1+a[u].f3)>a[v].dpf1)
{
a[v].dpf2=a[v].dpf1;
// a[v].dpg2=a[v].dpg1;
a[v].dpf1=max(a[u].dpf1,a[u].f1+a[u].f3);
a[v].dpg1=u;
}
else if(max(a[u].dpf1,a[u].f1+a[u].f3)<=a[v].dpf1&&max(a[u].dpf1,a[u].f1+a[u].f3)>a[v].dpf2)
{
a[v].dpf2=max(a[u].dpf1,a[u].f1+a[u].f3);
// a[v].dpg2=u;
}- 情况三+情况四:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//统一的对于链换根:
if(a[u].f1+1>a[v].f1)
{
a[v].f3=a[v].f2;
// a[v].g3=a[v].g2;
a[v].f2=a[v].f1;
a[v].g2=a[v].g1;
a[v].f1=a[u].f1+1;
a[v].g1=u;
}
else if(a[u].f1+1<=a[v].f1&&a[u].f1+1>a[v].f2)
{
a[v].f3=a[v].f2;
// a[v].g3=a[v].g2;
a[v].f2=a[u].f1+1;
a[v].g2=u;
}
else if(a[u].f1+1<=a[v].f2&&a[u].f1+1>a[v].f3)
{
a[v].f3=a[u].f1+1;
// a[v].g3=u;
}三、新的根 \(v\) 既不为父节点 \(u\) 的最长链的最优子节点 \(g1\),又不为父节点 \(u\) 的次长链的次优子节点 \(g2\):
- 情况五:新的根 \(v\) 为父节点 \(u\) 的最长子树路径的最优子节点 \(dpg1\):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//统计答案:
ans=max(ans,max(max((a[u].f1+a[u].f2)*a[v].dpf1,a[u].dpf2*a[v].dpf1),max((a[u].f1+a[u].f2)*(a[v].f1+a[v].f2),a[u].dpf2*(a[v].f1+a[v].f2))));
//对于子树路径换根:
if(max(a[u].dpf2,a[u].f1+a[u].f2)>a[v].dpf1)
{
a[v].dpf2=a[v].dpf1;
// a[v].dpg2=a[v].dpg1;
a[v].dpf1=max(a[u].dpf2,a[u].f1+a[u].f2);
a[v].dpg1=u;
}
else if(max(a[u].dpf2,a[u].f1+a[u].f2)<=a[v].dpf1&&max(a[u].dpf2,a[u].f1+a[u].f2)>a[v].dpf2)
{
a[v].dpf2=max(a[u].dpf2,a[u].f1+a[u].f2);
// a[v].dpg2=u;
}- 情况六:新的根 \(v\) 不为父节点 \(u\) 的最长子树路径的最优子节点 \(dpg1\):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//统计答案:
ans=max(ans,max(max((a[u].f1+a[u].f2)*a[v].dpf1,a[u].dpf1*a[v].dpf1),max((a[u].f1+a[u].f2)*(a[v].f1+a[v].f2),a[u].dpf1*(a[v].f1+a[v].f2))));
//对于子树路径换根:
if(max(a[u].dpf1,a[u].f1+a[u].f2)>a[v].dpf1)
{
a[v].dpf2=a[v].dpf1;
// a[v].dpg2=a[v].dpg1;
a[v].dpf1=max(a[u].dpf1,a[u].f1+a[u].f2);
a[v].dpg1=u;
}
else if(max(a[u].dpf1,a[u].f1+a[u].f2)<=a[v].dpf1&&max(a[u].dpf1,a[u].f1+a[u].f2)>a[v].dpf2)
{
a[v].dpf2=max(a[u].dpf1,a[u].f1+a[u].f2);
// a[v].dpg2=u;
}- 情况五+情况六:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//统一的对于链换根:
if(a[u].f1+1>a[v].f1)
{
a[v].f3=a[v].f2;
// a[v].g3=a[v].g2;
a[v].f2=a[v].f1;
a[v].g2=a[v].g1;
a[v].f1=a[u].f1+1;
a[v].g1=u;
}
else if(a[u].f1+1<=a[v].f1&&a[u].f1+1>a[v].f2)
{
a[v].f3=a[v].f2;
// a[v].g3=a[v].g2;
a[v].f2=a[u].f1+1;
a[v].g2=u;
}
else if(a[u].f1+1<=a[v].f2&&a[u].f1+1>a[v].f3)
{
a[v].f3=a[u].f1+1;
// a[v].g3=u;
}
\(\qquad\!\!\)最后,上代码(懒得写注释了,自己去上面找吧……):
1 |
|