SP6717 TWOPATHS - Two Paths & CF14D Two Paths
题目
求选出两条不相交树链的长度的最大乘积 数据范围1e5。
(CF那个是弱化版,数据范围 1000)
分析
首先弱化版的思路很暴力,就是这样想:选出来的两条路径一定被某一条边分开,正确性显然。
直接枚举断边,然后就可以直接对于两棵树分别求直径拼起来,求这个最大值即可。
接下来考虑 \(O(n)\) ,是大力换根 \(dp\) ,也是考虑每一条边断开,但是这时并不需要把两边分别再来求而是直接询问。
设 \(ilen[i]\) 表示:在 \(i\) 子树内的最长路径(可以经过 \(i\)),也就是 \(i\) 子树内的直径。
然后设 \(olen[i]\) 表示:在 \(i\) 子树外的最长路径(不可以经过 \(i\)),也就是原树去掉 \(i\) 子树后的直径。
那么答案显然就是 \(\max\limits_{i=2}^n\{ilen[i]\times olen[i]\}\) 。
考虑怎么求这两个数组。
首先求第一个 \(ilen[i]\)。
显然,我们可以直接按照普通求直径的办法使用树形dp求出这个dp值。
具体需要设 \(idis1[x],idis2[x],ilen1[x]\) ,分别表示 \(x\) 向下延伸的最长路和次长路(显然必须从 \(x\) 开始),以及 \(x\) 某个子树内的最长路径长度。
然后显然有 \(ilen[x]=max(idis1[x]+idis2[x],ilen1[x])\) ,剩下三者的转移显然。
接下来考虑求 \(olen[i]\) 。
显然这就是个换根dp,因为需要从父亲方向扩展过来。
我们有以下几种方案可能得出最大的 \(olen[x]\) (我们就是要取这几种情况的 \(\max\)) :
先设 \(odis[x]\) 表示从 \(x\) 开始向子树外走出去的最长路径。
第一种转移:直接继承父亲的子树外最长直径。
第二种转移:父亲往外走的最长路径拼接上一条从父亲开始往子树内走的路径(注意这个父亲往子树内走的路径可能是最长的也可能是次长的)。
显然是用最长路去拼接最优。但是这样有个问题,就是这个“父亲往其子树内走的最长路径”是有可能经过 \(x\) 的,所以此时我们需要记录这个“最长路径”是父亲的哪个点转移过来的(设为 \(idis1c[f]\)),如果这个值等于 \(x\) ,那么就需要用“父亲往其子树内走的次长路径”(也就是 \(idis2[f]\) 去拼接)
第三种转移:用两条“父亲往其子树内走的路径”拼接起来。
和刚刚上面有同样的问题,虽然显然是取最长和次长最优,但是都有可能经过 \(x\) ,于是我们还要记录第三场的路径(\(idis3[x]\)) ,以及次长路径的决策点(\(idis2c[x]\)) ,然后判断后选三者当中的两条合法的转移。
第四种转移:父亲子树内,但是在 \(x\) 子树外的本身就有的直径。
同样要考虑这个直径是不是 \(x\) 内的,所以需要记录最长的直径的决策点,也就是 \(ilen1[x]\) 的决策点 \(ilen1c[x]\) ,并且保存 \(ilen2[x]\) 来备选(次长直径)。
然后我们刚刚设的 \(odis[x]\) 还没求,还是考虑怎么dp出来:
第一种:直接继承父亲的路径。
第二种:从父亲子树内除 \(x\) 子树当中选取从父亲节点向下走的最长路,拼到 \(x\) 上。
这里还是要注意转移,父亲的这个路径不能是 \(x\) ,是的话就用次长路。
然后所有的都可以求出来了。
还有转移不懂的可以看代码。
代码
#include<bits/stdc++.h>
using namespace std;
inline char gc(){
// static char buf[100000],*p1=buf,*p2=buf;
// return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
return getchar();
}
template <typename T>
inline void read(T &x){
x=0;char ch=gc();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=gc();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
#define PII pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N=1e5+5,INF=1e9+7;
int n,m;
int head[N],to[N<<1],nex[N<<1],idx;
inline void add(int u,int v){nex[++idx]=head[u];to[idx]=v;head[u]=idx;return ;}
int idis1[N],idis1c[N],idis2[N],idis2c[N],idis3[N],ilen1[N],ilen1c[N],ilen2[N],ilen[N],olen[N],odis[N];
void dfs1(int x,int f){
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(y==f) continue;
dfs1(y,x);
//以下是求从x开始,到x子树内的最长/次长/第三长路径,以及对应决策点。
if(idis1[y]+1>idis1[x]){
idis3[x]=idis2[x];
idis2[x]=idis1[x],idis2c[x]=idis1c[x];
idis1[x]=idis1[y]+1,idis1c[x]=y;
}
else if(idis1[y]+1>idis2[x]){
idis3[x]=idis2[x];
idis2[x]=idis1[y]+1,idis2c[x]=y;
}
else if(idis1[y]+1>idis3[x]) idis3[x]=idis1[y]+1;
//以下是求x某个子树内的最长直径和次长直径以及对应决策点。
if(ilen[y]>ilen1[x]) ilen2[x]=ilen1[x],ilen1[x]=ilen[y],ilen1c[x]=y;
else if(ilen[y]>ilen2[x]) ilen2[x]=ilen[y];
}
//求x子树内的直径
ilen[x]=max(idis1[x]+idis2[x],ilen1[x]);
return ;
}
void dfs2(int x,int f){
int res=0;
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(y==f) continue;
//这里的x就是f,y就是x
//以下是求从x开始,往x外面走的最长路(odis[x])
odis[y]=odis[x]+1;
if(idis1c[x]==y) odis[y]=max(odis[y],idis2[x]+1);
else odis[y]=max(odis[y],idis1[x]+1);
//以下是求x子树外面的最长路(olen[x])
int res=olen[x];//第一种转移,直接拿父亲的答案
//第二种转移,父亲往外走的然后选一条子树内的拼起来。
if(idis1c[x]==y) res=max(res,odis[x]+idis2[x]);
else res=max(res,odis[x]+idis1[x]);
//第三种转移,父亲选两条子树内的拼起来。
if(idis1c[x]==y) res=max(res,idis2[x]+idis3[x]);
else if(idis2c[x]==y) res=max(res,idis1[x]+idis3[x]);
else res=max(res,idis1[x]+idis2[x]);
//第四种转移,父亲子树内现成的直径。
if(ilen1c[x]==y) res=max(res,ilen2[x]);
else res=max(res,ilen1[x]);
olen[y]=res;//更新
dfs2(y,x);
}
return ;
}
ll Ans;
signed main(){
while(~scanf("%d",&n)){
idx=Ans=0;
for(int i=1;i<=n;i++) head[i]=idis1[i]=idis1c[i]=idis2[i]=idis2c[i]=idis3[i]=ilen1[i]=ilen1c[i]=ilen2[i]=odis[i]=olen[i]=0;
for(int i=1,u,v;i<n;i++) read(u),read(v),add(u,v),add(v,u);
dfs1(1,0);dfs2(1,0);
for(int i=2;i<=n;i++) Ans=max(Ans,1ll*ilen[i]*olen[i]);
write(Ans),putchar('\n');
}
return 0;
}

浙公网安备 33010602011771号