Tyvj4878:道路修建(环套树+单调队列)

本文介绍了一种解决环套树中两点间最短路径最大值问题的算法。通过寻找环并使用DFS处理每棵外向树,记录每个节点最长及次长链的深度。同时,利用单调队列来解决路径跨越环的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面
题意:给你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より,約束が好き

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值