[题解] 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找无 ...
随机推荐
- fuseki远程访问方法
./fuseki-server启动服务后,我们的服务只能是localhost访问,无法被其他人访问,那么 要怎么修改呢.很简单,把apche-jena-fuseki-3.10.0/run 下面的shi ...
- 洛谷 P2031 脑力达人之分割字串
题目传送门 解题思路: f[i]表示到第i位可获得的最大分割次数,对于每个f[i]都可由其符合条件的前缀转移过来,条件就是当前串除了前缀的剩余字符里有所给单词,然后一看,这不是在剩余字符里找有没有所给 ...
- Windows系统查看端口被那些进程使用
Windows系统查看端口被那些进程使用注:当前Windows为虚拟机环境1. 打开命令提示符:开始-运行-cmd2. 列出使用端口的PID: netstat -abno -a 显示所有连接和侦听端口 ...
- 和为S的连续正整数序列(双指针法)
题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他 ...
- 利用 Ruoyi 开发自己的业务管理系统__测试结构完成
前言铺垫不多说 (1)Ruoyi这个平台不错:如果你觉得你比Ruoyi的作者牛逼,你就不用看我这个文章了,你可以走了,因为我自认为比Ruoyi的作者要烂: (2)必须已经成功搭建Ruoyi,并能在自己 ...
- Spring MVC RedirectAttributes取值方法
RedirectAttributes是Spring mvc 3.1版本之后出来的一个功能,专门用于重定向之后还能带参数跳转的他有两种带参的方式:第一种: attr.addAttribute(" ...
- B. RGB plants
B. RGB plants time limit per test 2.0 s memory limit per test 64 MB input standard input output stan ...
- NO5 grep-head-tail命令
·*****grep:#过滤需要的内容(linux三剑客). -v:排除内容.eg:grep -v oldboy test.txt ·head: #头,头部.读取文 ...
- mybatis-plus 乐观锁
参见:https://mp.baomidou.com/guide/optimistic-locker-plugin.html#%E4%B8%BB%E8%A6%81%E9%80%82%E7%94%A8% ...
- 记录:JAVA抽象类、接口、多态
JAVA抽象类.接口.多态 1. 多态 定义 多态是同一个行为具有多个不同表现形式或形态的能力.(多态就是同一个接口,使用不同的实例而执行不同操作) 如何实现多态 继承和接口 父类和接口类型的变量赋值 ...