题面
题意:给你3000棵环套树,边长都为1,问两个点的最短距离的最大值。
根据环套树的套路,先把环找出来,然后dfs每棵外向树。对于每个点,记录l1为该点向下最长链的深度,l2为为该点向下次长链的深度,l1+l2就可能成为答案。这样就处理了路径在每棵树上的情况。
我们考虑路径跨越了环的情况。想到在吃鸡时跑毒的短边原则,最短路肯定在环的小半圈。
设环的长度为len,对于环上的第i个点和第j个点,j>i,最长路径为
l1[i]+l1[j]+min(j-i,len-j+i)。
我们把min拆出来,就等价于把环复制一遍,对于点i,和它的前len/2个点计算距离,距离为l1[i]+l1[j]+i-j。就是求一个滑动区间的l1[j]-j的最大值,就是一个单调队列了。
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))
typedef long long LL;
const int N=3030;
int n,m,u[N],v[N];
int to[N*2],nex[N*2],head[N],cnt;
int fa[N],l1[N],l2[N],c[2*N],num;
int ans;
bool cir[N],vis[N];
int q[2*N];
void add(int u,int v)
{
to[++cnt]=v;
nex[cnt]=head[u];
head[u]=cnt;
}
void dfs2(int x)
{
cir[x]=1;
l1[x]=l2[x]=0;
for(int h=head[x];h;h=nex[h])
if(!cir[to[h]])
{
dfs2(to[h]);
if(l1[to[h]]+1>l1[x])
{
l2[x]=l1[x];
l1[x]=l1[to[h]]+1;
}
else
if(l1[to[h]]+1>l2[x])
l2[x]=l1[to[h]]+1;
}
ans=max(ans,l1[x]+l2[x]);
}
void dfs(int x)
{
vis[x]=1;
for(int h=head[x];h;h=nex[h])
if(to[h]!=fa[x]&&!num)
{
if(vis[to[h]]&&fa[to[h]]!=x)
{
while(x!=to[h])
{
c[++num]=x;
cir[x]=1;
x=fa[x];
}
c[++num]=to[h];
cir[to[h]]=1;
for(int i=1;i<=num;i++)
dfs2(c[i]);
return;
}
fa[to[h]]=x;
dfs(to[h]);
}
}
int get(int x)
{
return l1[c[x]]-x;
}
int main()
{
cin>>n>>m;
for(int i=1;i<n;i++)
scanf("%d%d",&u[i],&v[i]);
while(m--)
{
mmst(head,0);
mmst(vis,0);
mmst(cir,0);
num=ans=cnt=0;
scanf("%d%d",&u[n],&v[n]);
for(int i=1;i<=n;i++)
add(u[i],v[i]),add(v[i],u[i]);
dfs(1);
if(!num)
dfs2(1);
for(int i=1;i<=num;i++)
c[i+num]=c[i];
int len=num/2,hh=1,tt=0;
for(int i=num-len+1;i<=num;i++)
{
while(tt>=hh&&get(q[tt])<=get(i))
tt--;
q[++tt]=i;
}
for(int i=num+1;i<=num*2;i++)
{
ans=max(ans,get(q[hh])+l1[c[i]]+i);
if(hh<=tt&&i-q[hh]>=len)
hh++;
while(tt>=hh&&get(q[tt])<=get(i))
tt--;
q[++tt]=i;
}
printf("%d\n",ans);
}
return 0;
}
昨晚看完了《我女友与…的惨烈…》,可能是我太喜欢夏川真凉了,整晚都感觉好虐,然后我今天就坏掉了。
Kisekiより,約束が好き