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

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

原题地址

其实是一道很坑的模板题

题意很简单——给定几个基环树的森林,求每棵基环树的直径长度之和

基环树的直径有两种情况

  1. 不经过环的,每一棵子树的直径中最长的
  2. 经过环的,环上的一部分加上对应的两颗子树的深度

如图:

在这里插入图片描述

对于第一种情况的话,计算出每棵树的直径求一个max就好了
第二种情况可能相对麻烦一点。。。

其实可以把以环上的点为根的所有子树的深度求出来,然后再在环上跑用单调队列维护的dp就好。
具体操作的话可以断环成链,再复制一倍的链长就可以了


这边才是重点

(手动滑稽
然鹅本题的坑点在于数据。。。第一次TLE,MLE什么的在正常不过了
首先是因为本题的数据范围是1e6,一不小心就会常数爆炸
MLE呢,是因为他会故意卡你dfs,然后爆栈(但是也有打迪法师没爆炸的神仙)

而我向来自带大常数和大内存…
第一遍的代码WA飞,T飞,还炸了内存…(还有丑飞

#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 1000010
#define INF 0x3f3f3f3f3f3f3f3f

using namespace std;
typedef long long LL;
struct Node{
    int to, nxt;
    LL len;
}e[N << 1];
struct Q{
    int x;
    LL v;
}q[N << 1];

int lst[N], fa[N], used[N], vis[N], hoop[N], hp[N << 1], des[N], cnt;
LL dp[N], hp_len[N << 1], sum_len[N << 1], ans;

inline void add(int u, int v, LL len) {
    e[++cnt].to = v;
    e[cnt].nxt = lst[u];
    e[cnt].len = len;
    lst[u] = cnt;
}

inline int gfa(int x) {
    if (fa[x] == x) return x;
    fa[x] = gfa(fa[x]);
    return fa[x];
}

inline void merge(int x, int y) {
    int f1 = gfa(x), f2 = gfa(y);
    if (f1 != f2) {
        if (f1 > f2) swap(f1, f2);
        fa[f2] = f1;
    }
}

inline int find_hoop(int x, int fa) {
    if (vis[x]) {
        hoop[x] = 1;
        return 1;
    }
    vis[x] = 1;
    for (int i = lst[x]; i; i = e[i].nxt) {
        int son = e[i].to;
        if (!(son != fa || des[x] == son && des[son] == x)) continue;
        int k = find_hoop(son, x);
        if (k == 1) {
            hp[++hp[0]] = x;
//			printf("%d\n", x);
            hp_len[hp[0]] = e[i].len;
            if (hoop[x]) return 2;
            else hoop[x] = 1;
            return 1;
        }
        if (k == 2) return 2;
    }
    return 0;
}

inline void tree_dp(int x, int fa) {
    for (int i = lst[x]; i; i = e[i].nxt) {
        int son = e[i].to;
        if (son == fa || hoop[son]) continue;
        tree_dp(son, x);
        ans = max(ans, dp[x] + dp[son] + e[i].len);
        dp[x] = max(dp[x], dp[son] + e[i].len);
    }
}

inline LL make_ans(int x) {
//	printf("%d\n", x);
    memset(hp, 0, sizeof hp);
    memset(dp, 0, sizeof dp);
    memset(q, 0, sizeof q);
    find_hoop(x, x);
    LL ans = 0;
    for (int i = 1; i <= hp[0]; ++i) {
        tree_dp(hp[i], hp[i]);
        ans = max(ans, dp[hp[i]]);
    }
    for (int i = 1; i <= hp[0]; ++i) {
        hp[i + hp[0]] = hp[i];
        hp_len[i + hp[0]] = hp_len[i];
    }
//	for (int i = 1; i <= hp[0] * 2; ++i) printf("%d ", hp[i]);
//	puts("");
//	for (int i = 1; i <= hp[0] * 2; ++i) printf("%d ", hp_len[i]);
    int h = 1, t = 0;
    q[0].v = INF;
    for (int i = 1; i <= hp[0] * 2; ++i) {
        while (i - q[h].x >= hp[0]) ++h;
        q[h - 1].v = INF;
        sum_len[i] = sum_len[i - 1] + hp_len[i];
//		printf("~~%d~~\n", q[h].x);
        LL val = dp[hp[i]] - sum_len[i];
        while (q[t].v <= val) --t;
        q[++t].v = val;
//		printf("%d\n", q[t].v);
        q[t].x = i;
        ans = max(ans, sum_len[i] - sum_len[q[h].x] + dp[hp[q[h].x]] + dp[hp[i]]);
    }
    return ans;
}

int main() {
    int n;
    LL l;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) fa[i] = i;
    for (int i = 1; i <= n; ++i) {
        scanf("%d%lld", &des[i], &l);
        add(des[i], i, l);
        add(i, des[i], l);
        merge(i, des[i]);
    }
    for (int i = 1; i <= n; ++i)
        fa[i] = gfa(i);
    LL ansall = 0;
    for (int i = 1; i <= n; ++i) {
        if (!used[fa[i]]) {
            ansall += make_ans(fa[i]);
//	printf("%lld\n", ansall);
//			puts("-----------------");
            used[fa[i]] = 1;
        }
    }
    printf("%lld\n", ansall);
    return 0;
}

经过一晚上的思路整理,emm发现本题基环树的读入有一个很好的地方
我们可以把每组输入钦定为有向边,然后显然最后的形态一定是这样的:

在这里插入图片描述

用拓扑排序就能很快把非环的部分筛掉
(我用了个并查集来维护了每棵基环树,其实貌似也不需要的样子
最后一样的在环上跑dp就可以了

AC代码非常非常精炼(因为压行了…

#include <cstdio>
#include <algorithm>
#define N 1000010

using namespace std;
typedef long long LL;
struct Node { LL v; int x; } q[N << 1];
int fa[N], des[N], din[N], quq[N], vis[N]; LL len[N], ans[N], d[N];
inline int gfa(int x) { if (fa[x] == x) return x; fa[x] = gfa(fa[x]); return fa[x]; }

inline void merge(int x, int y) {
	register int fx = gfa(x), fy = gfa(y);
	if (fx != fy) { if (fx > fy) swap(fx, fy); fa[fy] = fx; }
}

int main() {
	int n; scanf("%d", &n);
	for (register int i = 1; i <= n; ++i) fa[i] = i;
	for (register int i = 1; i <= n; ++i) {
		scanf("%d%lld", &des[i], &len[i]); din[des[i]]++;
	}
	register int h = 0, t = 0;
	for (register int i = 1; i <= n; ++i) if (!din[i]) quq[++t] = i;
	while (h < t) {//拓扑
		++h; register int pt = des[quq[h]];
		ans[pt] = max(max(ans[quq[h]], ans[pt]), d[pt] + d[quq[h]] + len[quq[h]]);
		d[pt] = max(d[pt], d[quq[h]] + len[quq[h]]);
		din[pt]--; if (!din[pt]) quq[++t] = pt;
	}
	for (int i = 1; i <= n; ++i) if (din[i]) merge(i, des[i]);
	for (register int i = 1; i <= n; ++i) fa[i] = gfa(i);
	LL ansall = 0;
	for (register int i = 1; i <= n; ++i) {
		if (din[i] && !vis[fa[i]]) {
			vis[fa[i]] = 1;
			register int j = fa[i], h = 1, t = 0; LL sum = 0, val, anss = 0;
			do {
				sum += len[j]; j = des[j]; anss = max(anss, ans[j]);
				if (t) anss = max(anss, q[1].v + sum + d[j]);
				val = d[j] - sum; while (t > 0 && q[t].v <= val) --t;
				q[++t].v = val; q[t].x = j;
			} while (j != fa[i]);
			do {//两倍链长
				sum += len[j]; j = des[j];
				if (q[h].x == j) ++h; if (t) anss = max(anss, q[h].v + sum + d[j]);
				val = d[j] - sum; while (t >= h && q[t].v <= val) --t;
				q[++t].v = val; q[t].x = j;
			} while (j != fa[i]);
			ansall += anss;
		}
	}
	printf("%lld\n", ansall);
	return 0;
}
【算法笔记】基环树 基环树,也是环套树,是一种有 nnn 个点 nnn 条边的图,简单地讲就是树上在加一条边。它形如一个环,环上每个点都有一棵子树的形式。 基环内向树:每个点出度为1(因此每个环上点的子树,儿子指向父亲) 基环外向树:每个点入度为1(因此每个环上点的子树,父亲指向儿子) 基环树的关键就是找到环,可以先把环当作这个无根树的 “根” ,也就是把环缩点(先不管它),这样一颗基环树就变成了一个普通的树,然后我们先按照解决普通树的方法对“根”的所有子树依次处理求解答案,最后在单独对环上所有的点进行操作求解最终答案即可。 阅读详情

相关推荐

ANSYS Workbench实战:用网格自适应搞定超弹性橡胶大变形仿真(附避坑命令流)

本文深入探讨了ANSYS Workbench中超弹性橡胶大变形仿真的网格自适应技术,重点解析了混合U-P单元的应用、非线性自适应网格的实战技巧及材料模型选择的注意事项。通过具体案例和命令流示例,帮助工程师有效解决橡胶仿真中的收敛问题和网格畸变挑战,提升仿真精度和效率。

weixin_42666036的博客 303

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

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

修电缆的建筑工的博客 1132

保姆级教程:在Ubuntu 20.04上从零跑通AT128雷达实时点云(附PandarView 2安装避坑指南)

本文提供了一份详细的Ubuntu 20.04系统下AT128雷达实时点云配置教程,涵盖环境准备、脚本修改、PandarView 2安装及常见问题排查。通过逐步指导,帮助开发者解决权限、网络配置等难题,确保顺利实现雷达数据的实时可视化。特别附有PandarView 2安装避坑指南,适合新手快速上手。

weixin_30813225的博客 299

基环树(环套树) 总结

基环树(环套树) 总结

glorious_dream的博客 3806

电商产品组合策略:引流品与利润品的实战方法论

在电商运营中,产品组合策略是提升流量转化与利润增长的核心技术。其基本原理是通过数据化选品构建产品矩阵,利用引流品吸引用户流量,再通过利润品实现商业变现。这种策略不仅能优化广告投放的ROAS(广告投入产出比),还能显著提升客单价。技术实现上需要结合Google Analytics等工具进行归因分析,并运用动态库存管理模型控制风险。该方案特别适用于Amazon、Shopify等电商平台,能有效解决跨境电商业内流量获取与利润创造的平衡难题。通过实战案例可见,合理的产品组合可使客单价提升30-50%,是电商运营必备

weixin_42522009的博客 351

【机械视觉】Halcon—【二、Halcon算子全面介绍(超详细版)】

Halcon 的算子(operators)按照功能被系统性地划分为多个类别,官方文档中目前(Halcon 22.11 版本)共有 19 个主分类,每个主分类下还有若干子分类。 本人在此对这19个分类的常用核心算子进行了一系列的总结,且本人在文章最后还总结了一些案例经验。 注意: ​​​​​​​跨分类调用:某些算子可能在多个分类中,但官方文档中会明确其主分类。 例如: read_image(文件操作)常被其他分类(如图像处理)的示例调用,但其核心功能是文件读取。

qq_3517289697的博客 3919

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

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

haobowen的博客 2473

ENVI_基于无人机数据的果树提取与相对健康监测

一、数据处理 1.计算植被指数VDVI或GDVI:Band Math / Spectral Indices 2.制作掩膜:ROI——Threshold——Subset 二、单地块果树提取 1.绘制感兴趣区shp 2.确定果树提取参数:Count Crops 3.输出果树矢量文件:Convert Crops To Shapefile 4.果树相对健康程度分析:Calculate Crop Me...

提供技术服务、资源分享 429690771 1400

学习笔记-基环树入门

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

caoyang1123的博客 1290

RAP 语义键:让 UUID 世界变得可读、可导航、可校验

RAP 语义键的核心价值与实现要点 语义键(Semantic Key)是 ABAP RAP 模型中连接技术实现与业务体验的关键设计:通过为实体指定一组业务可读的标识字段(如订单号、物料编码等),在保持技术主键(如UUID)不变的同时,显著提升用户界面的可读性和导航体验。 主要优势: 提升UI可读性:Fiori元素会突出显示语义键字段,帮助用户快速识别关键业务数据 改善导航体验:OData URL会使用可读的语义键而非技术ID,便于分享和排查问题 降低沟通成本:业务人员能准确引用有意义的业务标识而非技术ID

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 155

工业质检终极方案:C#上位机融合 YOLO+Blob分析(一站式尺寸测量+缺陷检测)

本次上位机界面严格遵循工业现场操作规范简洁、直观、按钮大、文字清晰、参数可视化调节,操作人员为车间工人/质检员,无需复杂操作,所有功能一目了然。所有控件均为WinForms原生控件,拖拽即可完成布局,无任何自定义控件,开发效率拉满,核心布局永久复用本次实战完成的C#上位机+YOLO+Blob融合质检方案,是工业规则零件全尺寸质检的终极落地方案,完美解决了单一检测方案的所有短板,具备以下不可替代的工业价值。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1344

P4381 [IOI2008]Island 基环树直径 单调队列优化DP

学习自https://www.luogu.org/blog/user52918/qian-tan-ji-huan-shu #include<iostream> #include<vector> #include<algorithm> #include<string> #include<stack> #include<cstri...

M_theory004的博客 198

P4381 [IOI2008]Island(基环树+单调队列优化dp)

P4381 [IOI2008]Island 题意:求图中所有基环树的直径和 我们对每棵基环树分别计算答案。 首先我们先bfs找环(dfs易爆栈) 蓝后我们处理直径 直径不在环上,就在环上某点的子树上 我们对于环上每个点的子树,跑一边dp求直径即可,顺带处理子树的最深深度(环上点到子树某个叶节点的最长距离)$dis[x]$ 在dfs求直径时顺带求直径的最大值(可能是整棵基环树...

dingqiongliang0363的博客 266

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

BZOJ1791: [Ioi2008]Island 岛屿

BZOJ1791: [Ioi2008]Island 岛屿 Description 你将要游览一个有N个岛屿的公园。 从每一个岛i出发,只建造一座桥。 桥的长度以Li表示。 公园内总共有N座桥。 尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走。 同时,每一对这样的岛屿,都有一艘专用的往来两岛之间的渡船。 相对于乘船而言,你更喜欢步行。 你希望所经过的桥的总长度尽可能的长,但...

weixin_30757793的博客 165

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

【BZOJ1791】【IOI2008】【基环树】island(status速度第一)

BZOJ1791题(IOI2008 island)运行速度第一的详解博客,就问你怕不怕?

辗转山河弋流歌 5299

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

bzoj1791: [Ioi2008]Island 岛屿 基环树

bzoj1791: [Ioi2008]Island 岛屿 Description 你将要游览一个有N个岛屿的公园。从每一个岛i出发,只建造一座桥。桥的长度以Li表示。公园内总共有N座桥。尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走。同时,每一对这样的岛屿,都有一艘专用的往来两岛之间的渡船。 相对于乘船而言,你更喜欢步行。你希望所经过的桥的总长度尽可能的长,但受到以下的限制。 • ...

maxtir的博客 605

树形dp 基环树直径 bzoj1791 ioi2008island

这个题真的好麻烦啊。。。 就是给一堆基环树然后求出他们的直径的和 我们首先不考虑环,然后对于每个点求出他所能走到的最大深度, 然后缩点,就成了一个面包圈一样的环了 然后脱环成链直接用单调队列dp就可以了 队列中维护dp的决策,由于决策是有单调性的。。。#include #include #include #include #include #define MAX

wbysr 2465

IOI2008 Island

IOI2008 Island 这道题没仔细去想直接点开的题解,看了一会儿后发现是基环森林求直径的模板题,于是自己码了一波。 具体做法 对于一颗树我们可以直接dfsdfsdfs求出直径,看题解求直径竟然只保存了最大值,觉得以前求直径保存最大和次大太low了,对于一看基环树,首先求出环的子树中的最大路径设为sis_isi​,接着把环的序列倍长(套路),求出环上从序列第一个点到其他点的路径长度前缀和,设...

Galaxy_yr的博客 308

bzoj 1791: [Ioi2008]Island 岛屿(基环树直径(递归扣环模板))

题目大意:给你一堆基环树,求这些基环树的直径和。 题解:求基环树的直径:按直径是否经过环分类讨论:对于不经过环的直径,就是环上的点为根节点所在的子树的直径,取最大值。经过环的直径:显然要求出环上每一个点到它的子树里的点的最远距离 disdisdis。将环复制一倍展开,然后枚举环上每一个点uuu作为终点,用单调队列优化找出 disv−sumvdis_v - sum_vdisv​−sumv​的最大值...

qq_41997978的博客 293

Luogu_P4381 [IOI2008]Island 基环树

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

dazhishu2366的博客 283

[bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)

bzojluogu 题意可能会很绕 一句话:基环树的直径。 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_x$ 之后记录环上边长前缀和$ns_i$ dp值为$max_{i,j}dp[i]+sum[i]+dp[j]-sum[j]$ $dp[j]-sum[j]$提出来进单调队列。 O(n)。 记得dfs改bfs。 #include<cstdi...

weixin_30783913的博客 176

洛谷 P4381 [IOI 2008] Island- 提高+/省选-

的桥,不过桥是可以双向行走的。同时,每对岛屿之间都有一艘专用的往来两岛之间的渡船。座桥以及它们的长度,按照上述的规则,计算你可以走过的桥的长度之和的最大值。第一个整数表示桥另一端的岛,第二个整数表示该桥的长度。注意,上述游览结束时,你不能再游览这个岛。注意,你不必游览所有的岛,也可能无法走完所有的桥。你可以假设对于每座桥,其端点总是位于不同的岛上。行由两个以单空格分隔的整数,表示由岛。仅包含一个整数,即可能的最大步行距离。,即公园内岛屿的数目。之间有两座不同的桥。,而你的总步行距离为。

weixin_37253733的博客 1308

【BZOJ1791】【IOI2008】【基环树】island(status第一速度)

  1791:[Ioi2008]Island岛屿 TimeLimit:20SecMemoryLimit:162MB Submit:908Solved:159 [Submit][Status] Description 你将要游览一个有N个岛屿的公园。从每个岛i出发,仅仅建造一座桥。桥的长度以Li表示。公园内总共同拥有N座桥。虽然每座...

aofan9566的博客 205

基环树的最长链

[IOI2008]Island 传送门 本题给定几个基环树的森林,求每棵基环树的直径长度之和 思路: 先单独考虑一棵基环树,先dfs找环(也可tarjan),先把每个环上的点向外扩展,找出它们每棵子树的直径,用树型DP处理,接着拆环成链,再复制一倍的链长,在环上跑,用单调队列维护即可,但是维护前还需要预处理下直径前缀和,否则会爆时间。 代码来自wss巨佬: #include<bits/std...

超级蒟蒻的博客 228

bzoj1791 [Ioi2008]Island 岛屿(求基环树直径,单调队列)

给定一个基环树森林,求每一棵基环树的直径,他们的和就是答案。 基环树的直径:先找到环,然后直径只有可能是 1.环上某一个点的子树的直径 2.环上某两个点之间的距离加上他们各自子树中最深的点的深度。 然后搜一搜就好了,情况2可以单调队列一下。 O(n)O(n)O(n) 我写的太丑了,bzoj跑不过去qaq #include <bits/stdc++.h> using namespace ...

Icefox的博客 589
上一篇: CH4302-Interval GCD(线段树+树状数组+GCD)
下一篇: [一道感觉做过的题]翻牌游戏
北路人
博客等级 码龄9年 21粉丝 62原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值