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的更多相关文章

  1. BZOJ1791: [Ioi2008]Island 岛屿

    BZOJ1791: [Ioi2008]Island 岛屿 Description 你将要游览一个有N个岛屿的公园. 从每一个岛i出发,只建造一座桥. 桥的长度以Li表示. 公园内总共有N座桥. 尽管每 ...

  2. bzoj1791: [Ioi2008]Island 岛屿 单调队列优化dp

    1791: [Ioi2008]Island 岛屿 Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 1826  Solved: 405[Submit][S ...

  3. IOI2008 island

    题目链接:[IOI2008]Island 题目大意:求基环树直径(由于题目的意思其实是类似于每个点只有一个出度,所以在每个联通块中点数和边数应该是相同的,这就是一棵基环树,所以题目给出的图就是一个基环 ...

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

    P4381 [IOI2008]Island 题意:求图中所有基环树的直径和 我们对每棵基环树分别计算答案. 首先我们先bfs找环(dfs易爆栈) 蓝后我们处理直径 直径不在环上,就在环上某点的子树上 ...

  5. bzoj千题计划114:bzoj1791: [Ioi2008]Island 岛屿

    http://www.lydsy.com/JudgeOnline/problem.php?id=1791 就是求所有基环树的直径之和 加手工栈 #include<cstdio> #incl ...

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

    [bzoj1791][ioi2008]Island 岛屿(基环树.树的直径) bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_ ...

  7. 【题解】Luogu P4381 [IOI2008]Island

    原题传送门 题意:求基环树森林的直径(所有基环树直径之和) 首先,我们要对环上所有点的子树求出它们的直径和最大深度.然后,我们只用考虑在环上至少经过一条边的路径.那么,这种路径在环上一定有起始点和终点 ...

  8. bzoj1791[IOI2008]Island岛屿(基环树+DP)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...

  9. BZOJ 1791: [IOI2008]Island 岛屿 - 基环树

    传送门 题解 题意 = 找出无向基环树森林的每颗基环树的直径. 我们首先需要找到每颗基环树的环, 但是因为是无向图,用tarjan找环, 加个手工栈, 我也是看了dalao的博客才知道tarjan找无 ...

随机推荐

  1. 四 String类

    1 关于String类

  2. Redis之datatype概述

    Redis支持的数据类型     String     List    Set    Sorted Set    Hashes    Bit array    HyperLogLog     Bina ...

  3. C++ 11 :override 关键字的使用

    override 关键字 作用:在成员函数声明或定义中, override 确保该函数为虚函数并覆写来自基类的虚函数. 位置:函数调用运算符之后,函数体或纯虚函数标识 "= 0" ...

  4. k8s 各种网络方案【转】

    网络模型有了,如何实现呢? 为了保证网络方案的标准化.扩展性和灵活性,Kubernetes 采用了 Container Networking Interface(CNI)规范. CNI 是由 Core ...

  5. 使用PYaudio录制音频和视频(自己)

    参考:https://blog.csdn.net/zhaoyun_zzz/article/details/84341801 音频录制:简洁版 import pyaudioimport waveimpo ...

  6. ROS大型工程学习(三) ROS常用命令行

    1.rosbag 对ros包进行操作的命令. (1)录制包: rosbag record -a //录制数据包,所有topic都录制 rosbag record /topic_name1 /topic ...

  7. flutter之VSCode下Flutter常用终端命令行

    https://www.cnblogs.com/lxlx1798/p/11049922.html 梁飞宇 [Flutter学习]之VSCode下Flutter常用终端命令行 Flutter 常用命令行 ...

  8. OLTP和OLAP区别详解

    Oracle属于OLTP类型,而gp数据库属于OLAP类型的.具体了解如下: 数据库系统一般分为两种类型,一种是面向前台应用的,应用比较简单,但是重吞吐和高并发的OLTP类型:一种是重计算的,对大数据 ...

  9. HDU 4960 Handling the past 2014 多校9 线段树

    首先确定的基本思想是按时间离散化后来建线段树,对于每个操作插入到相应的时间点上 但是难就难在那个pop操作,我之前对pop操作的处理是找到离他最近的那个点删掉,但是这样对于后面的peak操作,如果时间 ...

  10. 解决使用xampp无法通过ip访问的问题

    本地能通过127.0.0.1或者localhost访问,无法通过外网地址访问: win解决防火墙问题: 1.进入控制面板 -> 系统和安全 -> 2.window防火墙 - 允许其他程序通 ...