【洛谷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 难度更大,代码更加复杂。 阅读详情

相关推荐

卫星图像检测,分割,跟踪,超分辨率,数据集调研,以及论文笔记

目标检测,针对物体有很多类如桥,烟囱,罐子等,有静态图像下的检测,也就是图像源头多来自谷歌地图,但是前后帧没有任何联系,并且谷歌地图相对来说比较清楚,难度小一点,也有粗粒度以及较细粒度的检测,然后卫星视频的数据集较少,则可以通过前后帧的联系来辅助检测,并且卫星帧基本上只对车,船,飞机,火车进行检测,没有进行细粒度的检测即将车辆分类为卡车,皮卡,小轿车,但是关船,则可以分类为货船以及军舰,因为目前的卫星视频中小车实在太少了,难以进行更细粒度的检测。luojia 三号卫星。

zhangzhao147的博客 4707

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

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

修电缆的建筑工的博客 1132

伺服电机三环控制实战:从参数整定到系统优化

本文以实战视角,深入解析伺服电机三环控制(位置环、速度环、电流环)的参数整定与系统优化全流程。文章摒弃复杂理论,通过生动的比喻和分步调试指南,详细阐述了从电流环快速响应、速度环平滑调节到位置环精准定位的调试方法,并提供了常见问题排查与进阶优化技巧,帮助工程师有效提升伺服系统性能与稳定性。

weixin_29201933的博客 246

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

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

m0_52398496的博客 518

PICO SCOPE 二次开发

基于C#开发一个简单的PICO示波器的上位机工作,与MCC600运动控制卡一起,实现数据自动采集

nixoncheng的博客 1148

CV领域交叉注意力机制:QKV在图像特征增强中的核心作用解析

本文深入解析了CV领域中交叉注意力机制的核心组件QKV在图像特征增强中的关键作用。通过通俗类比和实战案例,阐述了Q(查询)、K(键)、V(值)如何协同实现动态权重分配,从而在目标检测、图像分割与生成等任务中显著提升模型性能与语义对齐能力。

weixin_29194817的博客 441

学习笔记-基环树入门

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

caoyang1123的博客 1290

Pointnet++环境配置(Windows11和ubuntu)及训练教程

PS:前两行命令是数据处理的命令,主要是进行数据读取和生成相应的训练数据格式,在Ubuntu上是正常运行的,在Windows上运行会失败,无法生成训练用的数据格式,所以最好在Ubuntu上训练,如果一定要用Windows,可以ubuntu上生成数据格式在导到Windows上,数据生成不需要安装torch。通过anaconda配置的环境,在官网找的命令安装的,版本是2.1.0+cu118。训练命令:我使用的pycharm,直接在pycharm中打开终端,运行命令。Python版本:3.7。

AIcurator的博客 2558

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

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

qq_52142857的博客 473

cesium单向流动线效果

在学材质的路上一去不复返了属实是,难也难在这。还请各位大佬多多指教~

就zhei? 689

【DRAM存储器六十六】DDR5介绍--训练相关之CA/CS Training

详述DDR5 CA/CS training过程细节

highman110的博客 199

Luogu_P4381 [IOI2008]Island 基环树

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

dazhishu2366的博客 283

29、高速低功耗采样保持放大器设计

本文详细介绍了适用于低功耗模数转换器的高速低电压采样保持放大器(SHA)的设计技术,包括传统SHA的局限性、采样技术分类以及全NPN型和BiCMOS型SHA的具体设计实现。同时,文章深入探讨了优化输入电容、采样开关驱动及低功耗设计等具体措施,并提供了10位ADC前端和无线通信应用中的实例分析,为相关领域的工程师提供有价值的参考。

weixin_30600615的博客 425

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

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

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

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

洛谷P4381 [IOI2008]Island

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

Ajwallet 600

树的直径,重心和基环树(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、付费专栏及课程。

余额充值