P4381 [IOI2008]Island
显然题目给的图构成一个基环树森林
对于每个基环树单独考虑,显然每个都走直径是最优的
考虑如何求出基环树的直径
把直径分为两种情况考虑,首先可以找出环
因为直径可能不在环边上,所以对每个环上节点的子树进行一遍 $dfs$,求出每个节点子树的直径
维护 $dis[x]$ 表示节点 $x$ 到叶子节点的最长路程,那么直径就是每个节点儿子的 $dis$ 中最大和次大的和
可以一遍循环动态维护最大和次大
直径也可能在环上
设环上两点 $x,y$ 的距离为 $d(x,y)$,那么就是求最大的 $dis[x]+dis[y]+d(x,y)$
这样复杂度是 $O(n^2)$,考虑优化
按照套路,考虑把环断成链:

维护一条链上的距离前缀和 $sum[\ ]$,设 $y$ 在 $x$ 后面,那么就是求直径就是 $dis[x]+dis[y]+sum[y]-sum[x]$
换一下,就是求对于每一个 $y$,求链上区间 $x\in(y-n,y)$ 的 $(dis[x]-sum[x])$ 最大值 $+(dis[y]+sum[y])$($n为环的节点数$)
显然这个东西我们可以单调队列优化
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=1e6+;
int fir[N],from[N<<],to[N<<],val[N<<],cntt;
inline void add(int a,int b,int c)
{
from[++cntt]=fir[a]; fir[a]=cntt;
to[cntt]=b; val[cntt]=c;
}
struct edge{
int v,w;//节点,边权
}To[N];//To存题目给出的数据
int n,tot,ring[N],d[N];//ring存环上节点,d存环上节点到下一环上节点的边的边权
ll ANS;
int fa[N];//存环节点的'父'节点
bool vis[N],p[N];//vis判断是否为走过的基环树节点,p判断是否是基环树环节点
void BFS(int x)//找环
{
tot=; vis[x]=; int t;
while()
{
t=To[x].v;
if(vis[t])//找到就一路回跳并更新ring,d,p
{
ring[++tot]=t; d[tot]=To[t].w; p[t]=;
for(int i=x;i!=t;i=fa[i])
p[i]=,ring[++tot]=i,d[tot]=To[i].w;
return;
}
vis[t]=; fa[t]=x; x=t;//否则就继续找
}
}
ll dis[N],res;//维护当前基环树直径
void dfs(int x,int f)//处理dis和子树直径最大值
{
vis[x]=;
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(p[v]||v==f) continue;
dfs(v,x); res=max(res,dis[x]+dis[v]+val[i]);
//此时dis[x]还没有dis[v]+val[i],所以res可以这样更新
dis[x]=max(dis[x],dis[v]+val[i]);//更新dis
}
}
int Q[N<<]; ll sum[N<<];
inline int id(int x) { return (x-)%tot+; }//把链节点换成环节点
inline ll calc(int x) { return dis[ring[id(x)]]-sum[x]; }
inline void solve()//单调队列
{
int l=,r=;
for(int i=;i<=(tot<<);i++)
{
sum[i]=sum[i-]+d[id(i)];
while(l<=r && i-Q[l]>=tot ) l++;
if(l<=r) res=max(res,calc(Q[l])+sum[i]+dis[ring[id(i)]]);//先更新res
while(l<=r && calc(i)>=calc(Q[r]) ) r--;//再更新队列
Q[++r]=i;
}
}
int main()
{
n=read(); int a,b;
for(int i=;i<=n;i++)
{
a=read(),b=read();
add(i,a,b); add(a,i,b);
To[i].v=a; To[i].w=b;
}
for(int i=;i<=n;i++)
{
if(vis[i]) continue;
BFS(i); res=;
for(int j=;j<=tot;j++) dfs(ring[j],);
solve(); ANS+=res;
}
printf("%lld",ANS);
return ;
}
P4381 [IOI2008]Island的更多相关文章
- P4381 [IOI2008]Island(基环树+单调队列优化dp)
P4381 [IOI2008]Island 题意:求图中所有基环树的直径和 我们对每棵基环树分别计算答案. 首先我们先bfs找环(dfs易爆栈) 蓝后我们处理直径 直径不在环上,就在环上某点的子树上 ...
- 【题解】Luogu P4381 [IOI2008]Island
原题传送门 题意:求基环树森林的直径(所有基环树直径之和) 首先,我们要对环上所有点的子树求出它们的直径和最大深度.然后,我们只用考虑在环上至少经过一条边的路径.那么,这种路径在环上一定有起始点和终点 ...
- 【Luogu】P4381 [IOI2008]Island
一.题目 Description 你将要游览一个有N个岛屿的公园.从每一个岛i出发,只建造一座桥.桥的长度以Li表示.公园内总共有N座桥.尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走.同时, ...
- bzoj1791: [Ioi2008]Island 岛屿 单调队列优化dp
1791: [Ioi2008]Island 岛屿 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 1826 Solved: 405[Submit][S ...
- IOI2008 island
题目链接:[IOI2008]Island 题目大意:求基环树直径(由于题目的意思其实是类似于每个点只有一个出度,所以在每个联通块中点数和边数应该是相同的,这就是一棵基环树,所以题目给出的图就是一个基环 ...
- bzoj千题计划114:bzoj1791: [Ioi2008]Island 岛屿
http://www.lydsy.com/JudgeOnline/problem.php?id=1791 就是求所有基环树的直径之和 加手工栈 #include<cstdio> #incl ...
- BZOJ1791: [Ioi2008]Island 岛屿
BZOJ1791: [Ioi2008]Island 岛屿 Description 你将要游览一个有N个岛屿的公园. 从每一个岛i出发,只建造一座桥. 桥的长度以Li表示. 公园内总共有N座桥. 尽管每 ...
- [题解] LuoguP4381 [IOI2008]Island
LuoguP4381 [IOI2008]Island Description 一句话题意:给一个基环树森林,求每棵基环树的直径长度的和(基环树的直径定义与树类似,即基环树上一条最长的简单路径),节点总 ...
- [bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)
[bzoj1791][ioi2008]Island 岛屿(基环树.树的直径) bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_ ...
随机推荐
- Build Path
------------siwuxie095 什么是 Build Path? 为什么使用 Build Path? 如: (1)创建一个Java工程:LearnBuildPath (2)点击 Next, ...
- 34-n的pi次方
链接:https://www.nowcoder.com/acm/contest/118/B来源:牛客网 题目描述 喜爱ACM的PBY同学遇到了一道数学难题,已知底数n,请你帮他准确的计算出结果a = ...
- jQuery--加一行减一行
效果: 知识点: 克隆--clone() 追加--append() 移除--remove() 代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 2 ...
- 2.sql分类
SQL DML 和 DDL 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL). SQL (结构化查询语言)是用于执行查询的语法.但是 SQL 语言也包含用于更新.插 ...
- Flask框架 之 上下文管理前戏
偏函数 自动传递参数 import functools def index(a1,a2): return a1 + a2 # 原来的调用方式 # ret = index(1,23) # print(r ...
- 10.Python之Ansible自动化运维常用模块
Ansible中文权威文档:http://www.ansible.com.cn/docs/ Ansible从入门到精通:https://www.bilibili.com/video/av3361175 ...
- docker搭建gitbook服务
Gitbook Gitbook简介 GitBook 是一个基于 Node.js 的命令行工具,可使用 Github/Git 和 Markdown 来制作精美的电子书,GitBook 并非关于 Git ...
- oracle数据库查询全系整理
oracle数据库方面的知识到今天已经整理了12篇.当然,这不是终点,这只是一个开始,希望我写的文章可以帮助更多初学数据库的童鞋快速上手,如果你觉得文章对你有帮助,那么恭喜你已经入门了,数据库里面的知 ...
- App测试从入门到精通之兼容性和回归测试
兼容性测试需要考虑的APP测试点 1.不同网络环境下的兼容性测试 2.不同手机操作系统兼容性测试 3.不同应用软件的兼容性测试 4.不同的容量大小的SIM卡之间的互相兼容测试 5.当安装杀毒软件时,应 ...
- Hackfive 使用TextSwitcher和ImageSwitcher实现平滑过渡
1. 应用场景: 通过向左和向右的导航按钮浏览日期列表 在日期选择空间中改变日期 倒计时始终 新闻刚要 2.用到的知识点是: TextSwitcher和ImageSwitcher Te ...