【洛谷P4381】Island【基环树】【树的直径】【单调队列】【dp】

算法竞赛——进阶指南——acwing 358. 岛屿 基环树求直径 由于每个点都要连一个边。 最终图会分成若干个联通块,每个联通块n个点,n条边。即是一个基环树。 求出每个联通块中任意两点距离最大值(即基环树的直径)求和即可。 一般基环树的问题围绕环来处理。 先把环看出一个点,就能转化为树上问题。 基环树的直径分两类:(把环当成根节点) 1.不在环上,在环的某个节点的子树中。 2.经过环上2点x,y。并延申至x,y所在子树。即dis(x,y)+D[x]+D[y] .D[x]为dp求直径时维护的,x到其子树中点的最远距离。 我们先求出基环树的环 :s1,s2. 阅读详情

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P4381
给出一个基环树森林,求每个基环树的最长路径长度之和。


思路:

神题!无限 o r z orz orz

首先,这是一个基环树森林,所以把每一棵基环树拆开来做。
对于任意一棵基环树,它的最长路径要么是以任意环上节点为根的子树的直径之和,要么是环上任意两个点之间的距离加上它们到达以它们为根的子树任意一点的最大路径和。
说人话,设 l e n [ x ] len[x] len[x]表示以 x x x为根的子树( x x x为环上的点)的直径, d i s [ x ] dis[x] dis[x]表示点 x x x到达最近的环上的点的距离, m a x d i s [ x ] maxdis[x] maxdis[x]( x x x为环上的点)表示对于任意 y y y在以 x x x为根的子树的 m a x { d i s [ y ] } max\{dis[y]\} max{dis[y]}, d i s t [ x ] [ y ] dist[x][y] dist[x][y]表示环上的第 x x x个点和第 y y y个点的距离,对于任意一棵基环树(环上的集合为 Q Q Q),它的最长路径就是

  • m a x { l e n [ Q [ i ] ] } max\{ len[Q[i]] \} max{len[Q[i]]}
  • m a x { d i s t [ Q [ x ] ] [ Q [ y ] ] + m a x d i s [ Q [ x ] ] + m a x d i s [ Q [ y ] ] } max\{ dist[Q[x]][Q[y]]+maxdis[Q[x]]+maxdis[Q[y]]\} max{dist[Q[x]][Q[y]]+maxdis[Q[x]]+maxdis[Q[y]]}

树的直径是可以 O ( n ) O(n) O(n)求的。找环在 O ( n ) O(n) O(n)中也没有问题。于是如何求出 m a x { d i s t [ Q [ x ] ] [ Q [ y ] ] + m a x d i s [ Q [ x ] ] + m a x d i s [ Q [ y ] ] } max\{ dist[Q[x]][Q[y]]+maxdis[Q[x]]+maxdis[Q[y]]\} max{dist[Q[x]][Q[y]]+maxdis[Q[x]]+maxdis[Q[y]]}就是当务之急。
所以现在的问题有两个:

  1. 如何 O ( n ) O(n) O(n)求出该式子
  2. d i s t [ x ] [ y ] dist[x][y] dist[x][y]的空间复杂度是 O ( n 2 ) O(n^2) O(n2),如何把空间压成 O ( n ) O(n) O(n)。

首先考虑第二个问题。我们发现,若我们记录一个 d i s t _ 1 [ i ] dist\_1[i] dist_1[i]表示点 i i i到点1顺时针的距离,那么原先的 d i s t [ x ] [ y ] dist[x][y] dist[x][y]就可以表示成 d i s t _ 1 [ y ] − d i s t _ 1 [ x ] dist\_1[y]-dist\_1[x] dist_1[y]−dist_1[x]。这样就可以把空间压缩成 O ( n ) O(n) O(n)。
对于第一个问题。我们需要让 m a x d i s [ x ] + d i s t [ i ] − d i s t [ x ] maxdis[x]+dist[i]-dist[x] maxdis[x]+dist[i]−dist[x]尽量大,所以可以破环为链,复制一段到最后面,然后就是单调队列的模板了。每次维护 i − q . f r o n t ( ) &lt; i-q.front()&lt; i−q.front()<环上的点的个数即可。
时间复杂度: O ( n ) O(n) O(n),非常 n b nb nb的神题 o r z o r z o r z orzorzorz orzorzorz


代码:

#include <cstdio>
#include <queue>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;

const ll N=1000010;
ll in[N],head[N],father[N],len[N],f[N],dis[N],dist[N],maxdis[N],Q[N];
ll n,x,y,tot,cnt,ans,sum,maxlen,lastlen;
bool vis[N];

struct edge
{
    ll next,to,dis;
}e[N*2];

void add(ll from,ll to,ll dis)
{
    e[++tot].to=to;
    e[tot].dis=dis;
    e[tot].next=head[from];
    head[from]=tot;
}

void topsort()
{
    queue<int> q;
    for (ll i=1;i<=n;i++)
        if (in[i]==1) q.push(i);
    while (q.size())
    {
        ll u=q.front(),v;
        q.pop();
        for (ll i=head[u];~i;i=e[i].next)
        {
            v=e[i].to;
            if (in[v]>1)
            {
                in[v]--;
                if (in[v]==1) q.push(v);
            }
        }
    }
}

void dp(ll x,ll fa,ll root)  //求树的直径
{
    father[x]=root;
    for (ll i=head[x];~i;i=e[i].next)
    {
        ll y=e[i].to;
        if (y==fa||in[y]>1) continue;
        dis[y]=dis[x]+e[i].dis;
        maxdis[root]=max(maxdis[root],dis[y]);
        dp(y,x,root);
        len[root]=max(len[root],f[x]+f[y]+e[i].dis);
        f[x]=max(f[x],f[y]+e[i].dis);
    }
}

void find()  //找环
{
    bool ok;
    do
    {
        ok=0;
        vis[Q[cnt]]=1;
        for (ll i=head[Q[cnt]];~i;i=e[i].next)
        {
            ll v=e[i].to;
            if (in[v]>1&&!vis[v])
            {
           		ok=1;
                Q[++cnt]=v;
                dist[cnt]=dist[cnt-1]+e[i].dis;
                break;
            }
            else if (in[v]>1) lastlen=e[i].dis;
        }
    }
    while (ok);
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%lld",&n);
    for (ll i=1;i<=n;i++)
    {
        scanf("%lld%lld",&x,&y);
        add(i,x,y);
        add(x,i,y);
        in[i]++;
        in[x]++;
    }
    topsort();
    memset(vis,0,sizeof(vis));
    for (ll i=1;i<=n;i++)
        if (in[i]>1) dp(i,0,i);  //求树的直径
    for (ll j=1;j<=n;j++)
        if (!vis[j]&&in[j]>1)
        {
            memset(Q,0,sizeof(Q));
            memset(dist,0,sizeof(dist));
            Q[1]=j;  //记录换上的点
            cnt=1;
            find();
            for (ll i=1;i<=cnt;i++)  //复制一份
            {
                Q[i+cnt]=Q[i];
                dist[i+cnt]=dist[i]+dist[cnt]+lastlen;
            }
            deque<int> q;
            sum=0;
            for (ll i=1;i<=2*cnt;i++)
            {
                while (q.size()&&i-q.front()>=cnt) q.pop_front();  //长度不能超过cnt
                if (q.size())
                    sum=max(sum,maxdis[Q[i]]+maxdis[Q[q.front()]]+dist[i]-dist[q.front()]);  //单调
                while (q.size()&&maxdis[Q[i]]>maxdis[Q[q.back()]]+dist[i]-dist[q.back()])
                    q.pop_back();
                q.push_back(i);
            }
            for (ll i=1;i<=cnt;i++)
                sum=max(sum,len[Q[i]]);
            ans+=sum;
        }
    printf("%lld\n",ans);
    return 0;
}
基环树 DP:学习笔记 基环树,是一个 N 个点和 N 条边的连通图,特征是图中有且仅有一个环。特别的,如果不连通且每个连通块的点数和边数都相等,那么这就是一个基环树森林。 基环树 DP,顾名思义,就是在一个基环树上 DP,或是 DP 的结构类似基环树。相对于正常的树型 DP,一般来说基环树 DP 难度更大,代码更加复杂。 阅读详情

相关推荐

告别命令行恐惧!用TBtools的MCScanX插件,5分钟搞定拟南芥和水稻的基因组共线性分析

本文介绍如何利用TBtools的MCScanX插件快速完成拟南芥和水稻的基因组共线性分析,无需命令行操作,5分钟即可生成专业可视化结果。通过图形化界面简化传统MCScanX的复杂流程,特别适合非生物信息背景的研究者,显著提升基因组数据分析效率。

dianzhuique8587的博客 377

树的直径&基环树&单调队列

树的直径 定义:树中最远的两个节点之间的距离被称为树的直径。 怎么求呢?有两种官方的算法(不要问官方指谁我也不晓得): 1.两次搜索。首先任选一个点,从它开始搜索,找到离它最远的节点x。然后从x开始搜索,找到离x最远的点y,那 么E(x, y)的长度就是树的直径。时间复杂度为O(n)。 2.树形dp。这种其实更好写。我们可以对于某个节点x,分别求出经过它的最长链的长度。怎么求呢?首先,枚举x...

修电缆的建筑工的博客 1132

SRAM compiler中 Number of banks 设置

的值时,需要考虑应用的性能需求、功耗要求和面积限制。设计者应根据具体的应用场景选择合适的bank数量,以优化整体系统性能。bank是一种用于分离存储器访问的设计结构,允许同时对不同的存储区域进行读写操作,从而提高存储器的并发访问性能。选项用于指定 SRAM 存储器的 并发存储区域。在 SRAM 编译器中,

hzmscut的博客 1872

洛谷-Directed Roads-(基环树总结)

就是给你一个n点n边的图,然后你可以让每条边定一个方向,使得这个图没有环。问你方案数同时取模。

m0_52398496的博客 518

opencv基础57-模板匹配cv2.matchTemplate()->(目标检测、图像识别、特征提取)

OpenCV 提供了模板匹配(Template Matching)的功能,它允许你在图像中寻找特定模板(小图像)在目标图像中的匹配位置。模板匹配在计算机视觉中用于目标检测、图像识别、特征提取等领域。: 首先,加载目标图像和要匹配的模板图像。: 选择适当的匹配方法,例如 cv2.TM_CCOEFF、cv2.TM_CCOEFF_NORMED、cv2.TM_CCORR、cv2.TM_CCORR_NORMED、cv2.TM_SQDIFF 或 cv2.TM_SQDIFF_NORMED。每种方法对应不同的匹配计算方式。

小海聊智造 1万+

DeepSeek-V4-Pro 正式版突袭上线:Agent 能力暴涨,DeepSWE 直接干翻 Claude Opus 4.8

8 月 12 日深夜,DeepSeek 悄悄在 API 文档里上架了一个新版本号——DeepSeek-V4-Pro-0813。第二天(13 日),这个版本被确认为 V4 Pro 正式版。没有发布会,没有预热,只改了版本号,就把整个 AI 圈的注意力又抢了过去。

2601_96613114的博客 839

学习笔记-基环树入门

定义:多一条边的树。 性质:有一个环,删掉这环的任意一条边就可以变成树。 题目做法:一般先求出环,然后把处理这个环上每一个节点的子树,最后处理这个环。 模板题:给定一棵基环树,边带权,求直径。 做法1:刚开始没有接触过基环树的问题,所以不知道怎么搞,不过乱搞一波后发现nlogn是可写的。首先,dfs找到基环树的那个环,然后处理环上每个点的子树,维护这个子树里的最长链和次长链,更新答案。然后...

caoyang1123的博客 1290

保姆级教程:用VESTA软件5分钟搞定纳米颗粒Wulff Construction建模(以Au金为例)

本文提供了一份详细的VESTA软件教程,指导用户如何在5分钟内完成纳米颗粒Wulff Construction建模,特别以金(Au)为例。从环境搭建、基础晶体结构创建到Wulff Construction设置和可视化优化,涵盖了全流程操作步骤和常见问题解决方案,适合科研新手快速上手。

weixin_29311017的博客 436

蓝桥杯--发现环(拓扑法求解)

题目描述 小明的实验室有 NN 台电脑,编号 1 \cdots N1⋯N。原本这 NN 台电脑之间有 N-1N−1 条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。 不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了 BUG。 为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗? 输入描述 输入范围: 第一行包含一个整数 NN 。 以下

qq_52142857的博客 473

华为od 面试八股文_前端_03_含答案

华为od 面试八股文_前端_03_含答案

MISAYAONE的博客 2130

3篇1章6节:R基础绘图之图形的保存和导出技巧

高质量图形导出是R语言用户在数据分析和科研工作中的重要技能。通过合理控制图形的分辨率、选择合适的颜色管理方案和文件格式,以及掌握批量保存图形的技巧,可以显著提升图形的展示效果和应用范围。这不仅有助于提高研究成果的展示质量,还能在各种专业场合中展现数据分析的精确性和美观性。

MD Code 1482

Luogu_P4381 [IOI2008]Island 基环树

Luogu_P4381 [IOI2008]Island ### 基环树 题目链接 题目描述的就是一个基环树森林 每次必须要用船的就是两棵基环树之间转移 考虑每个基环树的贡献 有两种可能 第一种是不在环上的一条最长的链,也就是直径 第二种是经过环 第一种很好处理,DP求直径 第二种则在DP的时候记录\(d[i]\)表示\(i\)能到达的最远距离 这样可以断环为链,\(d[i]+d[j]+d...

dazhishu2366的博客 283

LLC设计指南(一)第一章:别一上来就背公式,先把 LLC 的“脾气”摸清楚

很多工程师第一次学 LLC,都会有一种感觉:这个电源拓扑,好像不是在工作,而是在“跳舞”。Buck 很直观,开关一开,电感充电;开关一关,电感放电。Flyback 也好理解,先存能量,再把能量丢给副边。但 LLC 不一样。它不是硬邦邦地把能量“推过去”,而是让电感、电容、变压器一起形成一个谐振网络,让能量在里面来回摆动。控制器再通过调节开关频率,把这个摆动过程“驯服”成稳定输出。这也是 LLC 最迷人的地方:🎯。

weixin_43199439的博客 319

BZOJ1791/洛谷4381 Island 岛屿(基环森林直径和+单调队列优化DP+树形DP)

题意:基环树森林的各个树直径之和。 分析:基环树(树)森林直径模板,详见《算法竞赛进阶指南》P388-389。 代码: #include<iostream> #include<cstdio> using namespace std; const int u=1000010; int ver[2*u],edge[2*u],next[2*u],head[u],du[...

常胜将军的博客 486

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

luogu P4381 [IOI2008]Island

题面传送门 这是一道基环树直径的题目。 考虑对于一个基环树,只有两种情况:直径在环上,直径在半个环上与链上。 直径在环上直接搞就好了,另一种情况可以单调队列优化dp 代码实现: #include<cstdio> #include<cstring> #define max(a,b) ((a)>(b)?(a):(b)) using namespace std; int n,m,k,x,y,z,st,flag[1000039],flag2[1000039],q[2000039],he

fxt275307894a的博客 193

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

洛谷P4381 [IOI2008]Island

直到我做完此题,这应该是在做这个之前的最难的一道题了吧。 树的直径+topsort+单调队列dp

Ajwallet 600

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

树的直径,重心和基环树(2019.2.3)

基本概念(树的直径): 树是图论中的连通无环图,树上的任意两点间的路径是唯一的。树上的任意两点的距离是两点间路径的边权和。而树的直径(假设为MP)是树上的最长路径,M,P两点是树上距离最远的点。 算法(树的直径): 两次BFS或DFS:从树上的的任意一点C出发找到离它最远的一个点A,再从点A开始寻找离它最远的点B,AB的距离就是树的直径。 证明:只需要证明M点是树的直径的一个端...

qq_40474867的博客 503

基环树(环套树) 总结

基环树(环套树) 总结

glorious_dream的博客 3806

P4381 [IOI2008] Island (求基环树直径)

先找到这棵树的环,这个过程分为两步,第一步找到换上的一条边,可以利用类似网络流的存边方法给走过的边记录是否走过,给走过的点标记是否走过,当走一条没走过的边可以走到走过的点,说明这条边是环上的边,第二步从环上的点出发如果走一条没走过的边发现可以走到环上的点,说明这个点就在环上。对于第二种情况,这时直径会是,两个环上的点的子树中最长的链的长度相加再加上这两点在环上的最大距离,我们将每个环上的点的子树最长链的长度看作点权,就变成求环上两点的最大距离加上两点点权的和最大。,这个只要记下前面最大的。

Morning_Glory_JR的博客 562

图论学习笔记 - 关于树上环的那些事儿

基环树 众所周知,N 个点的树有 N-1 条边。若在树上任意添加一条边,则会形成一个环。除了环之外,其余部分由若干棵子树构成。 我们把这种 N 个点 N 条边的连通无向图,即在树上加一条边构成的恰好包含一个环的图,称为“基环树”。如果不保证连通,那么 N 个点 N 条边的无向图也可能是若干棵基环树组成的森林,简称为“基环树森林”。 在有向图中,我们也有类似的概念。N 个点、N 条边、每个节点有且只有一条入边的有向图就好像以“基环”为中心,有向外扩散的趋势,故称为“外向树”。N个点、N条边、每个节点有且

haobowen的博客 2473

[IOI2008]Island(基环树的直径+优化)

原题地址 其实是一道很坑的模板题 题意很简单——给定几个基环树的森林,求每棵基环树的直径长度之和 基环树的直径有两种情况 不经过环的,每一棵子树的直径中最长的 经过环的,环上的一部分加上对应的两颗子树的深度 如图: 对于第一种情况的话,计算出每棵树的直径求一个max就好了 第二种情况可能相对麻烦一点。。。 其实可以把以环上的点为根的所有子树的深度求出来,然后再在环上跑用单调队列维护的dp就好...

nan 1009

【图论·算法】基环树(+习题[NOIP2018]旅行)

有关基环树的环 基环树是一种图,它由一个环组成,环上每个点都是一棵树点树根,所以称为基环树。当然,一棵树上连一条边也会变成基环树。 基环树一般分成环和树来分别处理(显然环的处理较为麻烦),首先得找到环。 那么如何去处理基环树的环呢?我们考虑用搜索来解决。 代码如下: void find_loop(int u) { vis[u]=++tot; for (int i=0;i&lt;a[u].siz...

Ronaldo7_ZYB的博客 605

【洛谷 P5022】旅行【图论(基环树)】

解题思路 按照数据,分类讨论n-1=m和n=m的情况。 对于树上的情况(n=m),题目暗含条件:当我们到达一个点时,我们必须遍历完它的整个子树才能后退然后去到别的点。 此时,我们可以用vector来存储它的所有边然后按这条边的终点的编号从小到大排一下序,这样就可以保证字典序最小了。于是,我们用一个dfs按顺序遍历每一个节点,在dfs过程中把它们压入ans数组中,最后输出ans数组就可以了。 后四个子任务n=m,而整个图又是联通的,这说明,这个图是一个基环树。 基环树:可以理解成在一棵树上的两个节点...

在人间贩卖声音 308

【模板】基环树模板

原文地址:https://blog.youkuaiyun.com/weixin_44824275/article/details/93719776

Q755100802的博客 351

9.18 城市道路设计

题意 给定\(n\)个点\(n\)条边的连通图,要求删去一条边,使得剩下的图仍是一个连通图,并且图中距离最远的两个点的距离最小 解法 \(n\)个点\(n\)条边的连通图即为基环树 要删去一条边使得剩下的图仍是连通图,我们删除的一定是环上的边,剩下的图一定是一颗树 那么树上距离最远的两个点的距离就是树的直径 暴力枚举环上断边求直径是\(O(N^2)\)的,考虑优化 单独考虑环上的点,把...

bansai5236的博客 170

基环树(模板) 2876. 有向图访问计数

【代码】基环树(模板) 2876. 有向图访问计数。

2301_80882026的博客 332
上一篇: 【洛谷P1297】单选错位【期望】
下一篇: 【POJ2240】Arbitrage【负环】
stoorz1023
博客等级 码龄9年 101粉丝 595原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值