战略威慑【LCA】【树的直径】
题目:
思路:
我们可以暴力枚举其中一条路径,那么问题就是在剩余的路径中找到一条长度最长的来匹配。
所以其实就是暴力枚举+树的直径。
枚举完一条路径后,求出端点的,然后暴力标记已经使用过的的点。然后原本的一棵树就被分为了很多棵树。在每一个树中分别跑直径即可。
时间复杂度
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=210,LG=10;
int n,tot,ans,cnt,maxlen,head[N],f[N][LG+1],dep[N],g[N];
bool flag[N];
struct edge
{
int next,to;
}e[N*2];
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1; f[x][0]=fa;
for (int i=1;i<=LG;i++)
f[x][i]=f[f[x][i-1]][i-1];
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa) dfs(v,x);
}
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int i=LG;i>=0;i--)
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
for (int i=LG;i>=0;i--)
if (f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
void update(int x,int y)
{
flag[x]=flag[y]=1;
for (;x!=y;x=f[x][0])
flag[x]=1,cnt++;
}
void dp(int x,int fa)
{
flag[x]=1; g[x]=0;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa && !flag[v])
{
dp(v,x);
maxlen=max(maxlen,g[x]+g[v]+1);
g[x]=max(g[x],g[v]+1);
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfs(1,0);
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
{
memset(flag,0,sizeof(flag));
int LCA=lca(i,j);
cnt=maxlen=0;
update(i,LCA); update(j,LCA);
for (int i=1;i<=n;i++)
if (!flag[i]) dp(i,0);
ans=max(ans,cnt*maxlen);
}
printf("%d",ans);
return 0;
}