『Island 基环树直径』
<更新提示>
<第一次更新>
<正文>
Island(IOI 2008)
Description
你准备浏览一个公园,该公园由 N 个岛屿组成,当地管理部门从每个岛屿 i 出发向另外一个岛屿建了一座长度为 L_i 的桥,不过桥是可以双向行走的。同时,每对岛屿之间都有一艘专用的往来两岛之间的渡船。相对于乘船而言,你更喜欢步行。你希望经过的桥的总长度尽可能长,但受到以下的限制:
- 可以自行挑选一个岛开始游览。
- 任何一个岛都不能游览一次以上。
- 无论任何时间,你都可以由当前所在的岛 S 去另一个从未到过的岛 D。从 S 到 D 有如下方法:
- 步行:仅当两个岛之间有一座桥时才有可能。对于这种情况,桥的长度会累加到你步行的总距离中。
- 渡船:你可以选择这种方法,仅当没有任何桥和以前使用过的渡船的组合可以由 SS 走到 D (当检查是否可到达时,你应该考虑所有的路径,包括经过你曾游览过的那些岛)。
注意,你不必游览所有的岛,也可能无法走完所有的桥。
请你编写一个程序,给定 N 座桥以及它们的长度,按照上述的规则,计算你可以走过的桥的长度之和的最大值。
Input Format
第一行包含 N 个整数,即公园内岛屿的数目。
随后的 N 行每一行用来表示一个岛。第 i 行由两个以单空格分隔的整数,表示由岛 i 筑的桥。第一个整数表示桥另一端的岛,第二个整数表示该桥的长度 L_i。你可以假设对于每座桥,其端点总是位于不同的岛上。
Output Format
仅包含一个整数,即可能的最大步行距离。
Sample Input
7
3 8
7 2
4 2
1 4
1 9
3 4
2 3
Sample Output
24
解析
很多年前的一道老题了,题意即为:给定基环树森林,求各棵基环树的直径之和。
大体思路是这样的,对于每一颗基环树,可以先找到基环树的环\(loop_{1-k}\),设以\(loop_i\)为根的树中,以\(loop_i\)为起点的最长链长度为\(deep_i\),该树中的朴素直径为\(diameter_i\),那么这一棵基环树的答案一定就是:
\begin{cases}
\max\{diameter_i\}
\\ \max\{deep_i+deep_j+path(loop_i,loop_j)\}
\end{cases}
\]
对于第一个式子,我们直接用树形\(DP\)求树的直径即可。
对于第二个式子,\(deep\)我们显然可以直接\(dfs\)处理,然后我们断环为链并复制一倍,\(path(loop_i,loop_j)\)的长度我们可以改为\(sum_j-sum_i\),那么就是求
\]
把它当做\(DP\),枚举\(j\),单调队列维护\({deep_i-sum_i}\)单调性即可。
然后累加每一棵基环树的答案即可。
我的做题历程和一些我犯过的错误:
- 单调队列误解,用了单调栈
- 没有考虑二元环的情况,环上边权选择重复,需要特判
- 发现做动态规划不能遍历每一种情况,断环为链时需要将链复制放在原链的后面,在做动态规划才能遍历每一种情况,需要顺带改进单调队列,距离当前长度超过\(n\)时弹出队首
- 发现菊花图的情况很容易被卡,是因为没有判断之间以环上某一个点为根取该树的直径的情况,对于环上每一个点,做一遍\(DP\)找以该节点为根的树的直径,最后以最大值和原答案取\(max\)
- 没有对每一棵基环树中子树的朴素直径与\(DP\)得到的答案取\(max\),对于每一棵基环树,应该都取一个\(max\)来累加答案,而非放在最后取
然后就是还有一个点是被卡常的,至于怎么办,我也不知道。
\(Code:\)
#include<bits/stdc++.h>
#define mset(name,val) memset(name,val,sizeof name)
using namespace std;
const int N=1e6+10;
const long long INF=LONG_LONG_MAX;
int n,Last[N*2],t;long long ans,deep[N*2],Link[N*2],sum[2*N],f[N*2],ans_,F[N],D[N];
int dfn[N],a[N*2],loop[N],inloop[N],tot,cnt,fa[N],used[N],vis[N];
struct edge{int ver,next;long long val;}e[N*2];
inline void insert(int x,int y,long long v)
{
e[++t].val=v;e[t].ver=y;e[t].next=Last[x];Last[x]=t;
}
inline char Getchar()
{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void readll(long long &k)
{
long long x=0,w=0;char ch;
while(!isdigit(ch))w|=ch=='-',ch=Getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=Getchar();
k=(w?-x:x);
}
inline void read(int &k)
{
int x=0,w=0;char ch;
while(!isdigit(ch))w|=ch=='-',ch=Getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=Getchar();
k=(w?-x:x);
}
inline void input(void)
{
read(n);
for(register int i=1;i<=n;i++)
{
int y;long long v;
read(y);readll(v);
insert(i,y,v);
insert(y,i,v);
}
}
inline void reset(void)
{
mset(loop,0);
mset(inloop,0);
mset(Link,0);
mset(deep,0);
mset(a,0);
tot=0;cnt=0;ans_=0;
}
inline void find_loop(int u)
{
dfn[u]=++cnt;
for(register int i=Last[u];i;i=e[i].next)
{
int v=e[i].ver;
if(v==fa[u])continue;
if(dfn[v])
{
if(dfn[v]<dfn[u])continue;
loop[++tot]=v,inloop[v]=true;
for(;v!=u;v=fa[v])
loop[++tot]=fa[v],inloop[fa[v]]=true;
}
else fa[v]=u,find_loop(v);
}
}
inline long long dfs(int x)
{
long long res=0;
used[x]=true;
for(register int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(!used[y]&&!inloop[y])
res=max(res,dfs(y)+e[i].val);
}
return res;
}
inline void find_diameter(int x)
{
vis[x]=true;
for(register int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(!vis[y]&&!inloop[y])
{
find_diameter(y);
F[x]=max(F[x],D[x]+D[y]+e[i].val);
D[x]=max(D[x],D[y]+e[i].val);
}
}
ans_=max(ans_,F[x]);
}
inline void get_deep(void)
{
for(register int i=1;i<=tot;i++)
{
deep[i]=dfs(loop[i]);
find_diameter(loop[i]);
}
}
inline void init(void)
{
if(tot>2)
{
for(register int i=1;i<=tot;i++)
{
for(register int j=Last[loop[i]];j;j=e[j].next)
{
if((e[j].ver==loop[i-1]&&i>1)||(e[j].ver==loop[tot]&&i==1))
{
Link[i]=e[j].val;
a[i]=loop[i];
}
}
}
}
else
{
long long v1=0,v2=0;
for(register int i=Last[loop[1]];i;i=e[i].next)
{
if(e[i].ver==loop[2])
{
if(!v1)v1=e[i].val;
else
{
v2=e[i].val;
break;
}
}
}
Link[1]=v1;Link[2]=v2;
a[1]=loop[1];a[2]=loop[2];
}
for(register int i=tot+1;i<=2*tot;i++)
{
Link[i]=Link[i-tot];
a[i]=a[i-tot];
deep[i]=deep[i-tot];
}
for(register int i=1;i<=tot*2;i++)
sum[i]=sum[i-1]+Link[i];
}
inline long long calc(int x)
{
return deep[x]-sum[x];
}
inline long long dp(void)
{
deque < int > q;long long res=ans_;
q.push_back(1);res=max(res,deep[1]);
for(register int j=2;j<=2*tot;j++)
{
while(!q.empty()&&j-q.front()>=tot)q.pop_front();
f[j]=sum[j]+deep[j]+calc(q.front());
while(!q.empty()&&calc(q.back())<calc(j))q.pop_back();
q.push_back(j);
res=max(res,f[j]);
}
return res;
}
int main(void)
{
input();
for(int i=1;i<=n;i++)
{
if(!dfn[i])
{
reset();
find_loop(i);
get_deep();
init();
ans+=dp();
}
}
printf("%lld\n",ans);
return 0;
}
<后记>
『Island 基环树直径』的更多相关文章
- luogu 4381 [IOI2008]Island 单调队列 + 基环树直径 + tarjan
Description 你将要游览一个有N个岛屿的公园.从每一个岛i出发,只建造一座桥.桥的长度以Li表示.公园内总共有N座桥.尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走.同时,每一对这样 ...
- BZOJ1791 基环树直径
非递归版4S /************************************************************** Problem: 1791 User: 18357 Lan ...
- 【BZOJ1791】【IOI2008】【基环树】island(status第一速度)
1791: [Ioi2008]Island 岛屿 Time Limit: 20 Sec Memory Limit: 162 MB Submit: 908 Solved: 159 [Su ...
- bzoj1791[IOI2008]Island岛屿(基环树+DP)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...
- D4 树的直径、重心以及基环树
第一题第二题鉴上我前几篇博客poj1985 poj1849:https://www.cnblogs.com/Tyouchie/p/10384379.html 第三题:数的重心:poj1655 来自sj ...
- BZOJ1791[Ioi2008]Island 岛屿 ——基环森林直径和+单调队列优化DP+树形DP
题目描述 你将要游览一个有N个岛屿的公园.从每一个岛i出发,只建造一座桥.桥的长度以Li表示.公园内总共有N座桥.尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走.同时,每一对这样的岛屿,都有一 ...
- bzoj 1791: [Ioi2008]Island 岛屿【基环树+单调队列优化dp】
我太菜了居然调了一上午-- 这个题就是要求基环树森林的基环树直径和 大概步骤就是找环->dp找每个环点最远能到达距离作为点权->复制一倍环,单调队列dp 找环是可以拓扑的,但是利用性质有更 ...
- BZOJ1791 [Ioi2008]Island 岛屿[基环树+单调队列优化DP]
基环树直径裸题. 首先基环树直径只可能有两种形式:每棵基环树中的环上挂着的树的直径,或者是挂在环上的两个树的最大深度根之间的距离之和. 所以,先对每个连通块跑一遍,把环上的点找出来,然后对环上每个点跑 ...
- [IOI2008/BZOJ1791 岛屿](处理基环树的小技巧&基于bfs树形DP)
IOI2008/BZOJ1791 岛屿 题目大意是在一个基环树森林里求每一棵基环树的直径①的和. 其实就是树的直径的基环树升级版.我们先把环找出来,然后从环上的每一个节点x出发,并且不经过环上其他节点 ...
随机推荐
- Go语言初篇
Go语言初篇 目录 Go-开发环境 Go-语言基础 Go-标准库 Go-面向对象 Go-并发 Go-数据库 Go-web框架 Go语言开发文档:https://studygolang.com/pkgd ...
- 《团队作业》五小福团队作业--UNO-- LandingDay--降落
<团队作业>五小福团队作业--UNO-- LandingDay--降落 写在前面 几周的飞行之后,降落之日也如期而至了.在2018年12月19日我们顺利地完成了项目的总结汇报.但是,短暂的 ...
- x64类型的程序逆向思考
x64类型比较习惯ida去分析,需要注意的是在x64程序中,有时会因为自己对寄存器不太熟悉导致自己分析过程混淆,下面坐下简单记录
- Android- APP 秒开
Android- APP 秒开 1. 启动APP 时白屏或者黑屏 现象:当启动APP 时,会有一个白屏或者黑屏一闪而过,然后才会显示出主界面. 这是因为,我们新打开一个应用,系统会为这个应用创建一个进 ...
- Pycharm下同一目录的py文件不能相互调用的原因分析
1.首先确保所在目录是Python Package而不是一般的New Stratch File Python Package下有__init___.py或自己建空的__init___.py 2.pyc ...
- GitHub的简单使用记录
记录于:2013/4/24 GitHub(网址 https://github.com/)是一个面向开源及私有软件项目的托管平台,因为只支持Git作为唯一的版本库格式进行托管,故名GitHub. G ...
- [Linux] 使用Yum在CentOS上安装MySQL
跟随官网上的安装教程:https://dev.mysql.com/doc/refman/8.0/en/linux-installation-yum-repo.html官网上还有一个QuickGuide ...
- javascript是什么,可以做什么?
是一门脚本语言:不需要编译,直接运行 是一门解释型语言:遇到一行代码就解释一行代码 是一门动态类型的语言 是一门基于对象的语言 是一门弱类型的语言:声明变量的时候不用特别声明类型都使用var 不是一门 ...
- 用java从0生成一个简单的excel
用java从0生成一个简单的excel 目标 用代码实现对一个excel的基础操作,包括创建,插入文字,(好像就这些了),生成的excel可以用wps打开,如果直接用c++的文件流会生成假的xls表格 ...
- NOIP-珠心算
题目描述 珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术.珠心算训练,既能够开发智力,又能够为日常生活带来很多便利,因而在很多学校得到普及. 某学校的珠心算老师采用一种快速考察珠心算加 ...