题目链接
题意
给出图的笛卡尔积定义:点集做笛卡尔积,比如我们用数字123代表点集V1,字母abc代表点集V2,那么新图的点集V就是二元组(1,a)(1,b)(1,c)(2,a)…的集合。假如1-2,b-c有边,那么新图的边集就是(1,a)->(2,a),(1,b)->(2,b),(1,c)->(2,c),(1,b)->(1,c)…这样的集合。
定义δ(a,b) 为两点最短路, tr(G) 代表图G上任意三元组中3个δ和的最大值
给出n棵树,问做笛卡尔积后tr的值
思路
先理解题干,大概对两棵树做笛卡尔积后的形状(多层相连的一"排"图)有个直观的认识。大致猜测最终tr的值是各棵树 tr的 加/乘 的值,对着样例推了一遍(面向样例编程),发现最终tr的值是n棵树的tr值之和。
考虑单棵树,如果是求δ(a,b) 最大值显然是树的直径,如果改成三元组的话,我们令其中两点在直径上,最后一点选择离直径两端点距离加和最大值的点即可。直径很好求,套板子。至于离端点距离加和最大值,我们开两个dis数组,分别dfs一次(其中一次可以合并到求直径过程中)即可。
代码
dfs1:直径第一次dfs
dfs2:直径第二次dfs以及跑出dis1
dfs3:跑出dis2
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=500005;
const int inf=0x3f3f3f3f;
int n,m,k;
int head[maxn];
struct Edge{
int v,next;
}edge[maxn];
int cnt;
int ma,nexts;
int p1,p2;
int dis1[maxn],dis2[maxn];
void init(int x){
for(int i=0;i<=x;i++) head[i]=-1;
cnt=0;
}
inline void add(int u,int v){
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
inline void addd(int u,int v){
//cout<<u<<' '<<v<<endl;
add(u,v);add(v,u);
}
void dfs1(int s,int x,int fa){
if(x>ma){
ma=x;
p1=s;
}
for(int i=head[s];~i;i=edge[i].next){
if(edge[i].v!=fa)
dfs1(edge[i].v,x+1,s);
}
}
void dfs2(int s,int x,int fa){
if(x>ma){
ma=x;
p2=s;
}
dis1[s]=x;
for(int i=head[s];~i;i=edge[i].next){
if(edge[i].v!=fa)
dfs2(edge[i].v,x+1,s);
}
}
void dfs3(int s,int x,int fa){
dis2[s]=x;
for(int i=head[s];~i;i=edge[i].next){
if(edge[i].v!=fa)
dfs3(edge[i].v,x+1,s);
}
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
while(cin>>n){
ll ans=0;
while(n--){
cin>>m;
init(m);
for(int i=2;i<=m;i++){
int t;
cin>>t;
addd(i,t);
}
ma=0;
dfs1(1,0,-1);
ma=0;
dfs2(p1,0,-1);
ans+=ma;
//cout<<"直径"<<ma<<endl;
dfs3(p2,0,-1);
int maxx=-1;
for(int i=1;i<=m;i++)
maxx=max(maxx,dis1[i]+dis2[i]);
//cout<<"次长路"<<maxx<<endl;
ans+=maxx;
}
cout<<ans<<endl;
}
}