[题解] LuoguP4381 [IOI2008]Island
Description
一句话题意:给一个基环树森林,求每棵基环树的直径长度的和(基环树的直径定义与树类似,即基环树上一条最长的简单路径),节点总数不超过\(10^6\)。
Solution
问题就是如何求基环树的直径。
首先树的直径的话可以直接\(dp\),那如果有一个环怎么办?
这个环上会挂着几棵树,那么直径只会有两种情况
- 不经过环上的边,即每棵树直径的最大值
- 经过一个环,即挂在换上的两棵树\(i,j\)的深度和在加上\(i,j\)在环上的距离
第一种情况直接树形\(Dp\)求一下树的直径就好了。
第二种情况有点麻烦,为了方便下面令\(tree(x)\)表示以\(x\)为根挂在环上的树,\(depth(T)\)表示树\(T\)的深度,\(dist(i,j)\)表示环上两点之间只走环上的边的最大距离(\(i,j\)在环上只有两条路径)。
那这种情况的答案就是\(\max\limits_{i \not= j} \{depth(tree(i)) + depth(tree(j)) + dist(i,j)\}\)
下面令\(v_1,v_2,...,v_s\)表示大小为\(s\)(点的个数)的环上以逆时针或顺时针的访问顺序依次访问到的\(s\)个点,\(sum_i\)表示从\(v_1\)按顺序走走到\(v_i\)的环上路径长度。
那么点\(i\)按一个方向走到点\(j\)的环上长度就是\(sum_j - sum_i\)。
我们可以把环复制两倍,然后就能够处理第\(2\)个方向的距离。
即\(v\)变为\(v_1,v_2,...,v_{s},v_{s+1},...,v_{2s}\),那么\(v_i\)与\(v_j\)(\(1 \le i<j \le 2s, abs(i-j) < n\))的最大距离\(dist(i,j)\)就是\(max(sum_j - sum_i, sum_{i+s} - sum_j)\)
这样的话就可以单调队列维护,扫一次\(v_{1...2s}\)就行了。
找环的话可以在\(Dfs\)树上找,不卡栈空间的(至少\(Luogu\)是这样......)
Code
#include <bits/stdc++.h>
using namespace std;
template<typename tp> inline void read(tp &x){
x=0; tp f=1; char ch=getchar();
for (;!isdigit(ch);ch=getchar())f=ch=='-'?-f:f;
for (;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
x=x*f;
}
#define pb push_back
#define same(e1,e2) (min(e1^1,e1)==min(e2^1,e2))
typedef long long ll;
const ll INF=1e18;
const int N=2e6+10;
int cnt=1,fst[N],nxt[N<<1],to[N<<1];ll dis[N<<1];
inline void ade(int x,int y,ll w){
to[++cnt]=y,nxt[cnt]=fst[x],fst[x]=cnt;
dis[cnt]=w;
}
inline void addedge(int x,int y,ll w){ade(x,y,w),ade(y,x,w);}
vector<int>ring[N]; int tot=0,dep[N],fa[N];
void dfs(int x,int deep,int lste,int prev){
dep[x]=deep,fa[x]=prev;
for (int i=fst[x];i;i=nxt[i]){
int v=to[i]; //printf("%d->%d\n",x,v);
if (dep[v]==0) dfs(v,deep+1,i,x);
else if (!same(i,lste)&&dep[v]<dep[x]){
++tot;for (int nw=x;nw!=v;nw=fa[nw])ring[tot].pb(nw);
ring[tot].pb(v);
}
}
}
int mark[N];ll dp[N],mxdp;
void DP(int x,int prev){
for (int i=fst[x];i;i=nxt[i]){
int v=to[i]; if (mark[v]||v==prev) continue;
DP(v,x);
mxdp=max(dp[x]+dp[v]+dis[i],mxdp);
dp[x]=max(dp[x],dp[v]+dis[i]);
}
}
int vis[N],id[N],tim;ll a[N],b[N];
void getW(int x,ll dd,int lste){
vis[x]++,id[++tim]=x,b[tim]=dd;
for (int i=fst[x];i;i=nxt[i]){
int v=to[i]; if (!same(i,lste)&&mark[v]&&vis[v]<2)getW(v,dd+dis[i],i);
}
}
int q[N];
ll solve(int k1){
int len=ring[k1].size(); ll ans=0;
for (int i=0;i<len;i++) mark[ring[k1][i]]=1;
tim=0,getW(ring[k1][0],0,0);
for (int i=1;i<=len;i++){
int x=id[i];
mxdp=0,DP(x,0),ans=max(ans,mxdp);
a[i]=dp[x];
}
for (int i=1;i<=len;i++) a[i+len]=a[i];
int l=1,r=1; q[l]=1;
for (int i=2;i<=tim;i++){
while (l<=r&&i-q[l]>=len)l++;
int j=q[l]; if (l<=r)ans=max(ans,a[i]+a[j]+b[i]-b[j]);
while (l<=r&&a[i]-b[i]>a[q[r]]-b[q[r]])r--;
q[++r]=i;
}
return ans;
}
int main(){
int n;read(n);
for (int i=1;i<=n;i++){
int x;ll w; read(x),read(w);
addedge(x,i,w);
}
for (int i=1;i<=n;i++) if (!dep[i])dfs(i,1,0,0);
ll ans=0;
for (int i=1;i<=tot;i++)ans+=solve(i);
printf("%lld\n",ans);
return 0;
}
[题解] LuoguP4381 [IOI2008]Island的更多相关文章
- BZOJ1791: [Ioi2008]Island 岛屿
BZOJ1791: [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 题目大意:求基环树直径(由于题目的意思其实是类似于每个点只有一个出度,所以在每个联通块中点数和边数应该是相同的,这就是一棵基环树,所以题目给出的图就是一个基环 ...
- P4381 [IOI2008]Island(基环树+单调队列优化dp)
P4381 [IOI2008]Island 题意:求图中所有基环树的直径和 我们对每棵基环树分别计算答案. 首先我们先bfs找环(dfs易爆栈) 蓝后我们处理直径 直径不在环上,就在环上某点的子树上 ...
- bzoj千题计划114:bzoj1791: [Ioi2008]Island 岛屿
http://www.lydsy.com/JudgeOnline/problem.php?id=1791 就是求所有基环树的直径之和 加手工栈 #include<cstdio> #incl ...
- [bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)
[bzoj1791][ioi2008]Island 岛屿(基环树.树的直径) bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_ ...
- 【题解】Luogu P4381 [IOI2008]Island
原题传送门 题意:求基环树森林的直径(所有基环树直径之和) 首先,我们要对环上所有点的子树求出它们的直径和最大深度.然后,我们只用考虑在环上至少经过一条边的路径.那么,这种路径在环上一定有起始点和终点 ...
- bzoj1791[IOI2008]Island岛屿(基环树+DP)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...
- BZOJ 1791: [IOI2008]Island 岛屿 - 基环树
传送门 题解 题意 = 找出无向基环树森林的每颗基环树的直径. 我们首先需要找到每颗基环树的环, 但是因为是无向图,用tarjan找环, 加个手工栈, 我也是看了dalao的博客才知道tarjan找无 ...
随机推荐
- linux下mysql允许远程连接
1. MySql安装教程 https://dev.mysql.com/doc/refman/5.7/en/linux-installation-yum-repo.html 默认情况下mysq的 roo ...
- 百度地图javascript API,每个功能每天免费的次数
- zabbix proxy配置实战案例
zabbix proxy配置实战案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.zabbix proxy概述 上一篇博客我们分享了zabbix agent有两种工作模式,即 ...
- firewalld学习--service的使用和配置
service配置文件 firewalld默认给我们提供的ftp的service配置文件ftp.xml <?xml version="1.0" encoding=" ...
- mysql导入导出无权限
error:The MySQL server is running with the --secure-file-priv option so it cannot execute this state ...
- 055、Java中使用for循环输出乘法口诀表
01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...
- 开源免费的安卓投屏工具-Scrcpy
最近需要使用安卓投屏在桌面上操作,一开始使用Vysor,免费版画质无法直视,发现一个开源的工具,Scrcpy,貌似效果不错,但没有GUI,命令行安装,整起(Mac) 1.安装 homebrew: 通过 ...
- Vue点到子路由,父级,无法高亮问题解决
[问题] Vue点到子路由,父级,无法高亮 [原因]多是因为链接简写相对路径没有写完整导致 [解决]把子路由的router-link的to属性里链接写完整.并把router配置文件里path也写完整即 ...
- redis学习(四)
一.Redis 键(key) 1.Redis 键命令用于管理 redis 的键. 2.Redis 键命令的基本语法如下:redis 127.0.0.1:6379> COMMAND KEY_NAM ...
- HDU - 6197 array array array (最长上升子序列&最长下降子序列)
题意:对于一个序列,要求去掉正好K个数字,若能使其成为不上升子序列或不下降子序列,则“A is a magic array.”,否则"A is not a magic array.\n&qu ...