BZOJ 1023

如果我们把所有的环都缩成一个点,那么整张图就变成了一棵树,我们可以直接$dp$算出树的直径。

设$f_x$表示$x$的子树中最长链的长度,那么对于$x$的每一个儿子$y$,先用$f_x + f_y + 1$更新答案,再用$f_y + 1$更新$f_x$。

考虑加入环的情况,保留这个$f_x$的设定。我们可以按照搜索顺序把环上第一个搜到的点看成环的“根”,然后用这个“根”来计算这个环。

假设有环$1, 2, 3, ..., m$,$1$是环的“根”,那么我们可以用$f_i + f_j + min(j - i, m - (j - i))\ (i < j)$来更新答案,然后用$max(f_i + min(i - 1, m - (i - 1)))$来更新$f_1$。

发现这个$min$不怎么好更新,可以断环成链复制一倍,然后用单调队列滑动一个长度为$\left \lfloor \frac{m}{2} \right \rfloor$的区间即可。

在$dfs$的时候保留的$tarjan$时候采用的$dfn$和$low$数组,当$dfn_x < low_y$的时候说明走了一条桥边,按照原来的树形$dp$更新答案。

处理完所有子树之后重新扫一遍儿子,观察是否有$fa_y \neq x$并且$dfn_y > dfn_x$的点,如果有,那么$x$是环的“根”,$y$是环的另一个端点。

时间复杂度应该是$O(n)$吧。

Code:

#include <cstdio>
#include <cstring>
using namespace std; const int N = 5e4 + ;
const int M = 2e5 + ; int n, m, tot = , head[N], f[N], g[N << ], q[N << ];
int ans = , dfsc = , fa[N], dfn[N], low[N], dep[N]; struct Edge {
int to, nxt;
} e[M]; inline void add(int from, int to) {
e[++tot].to = to;
e[tot].nxt = head[from];
head[from] = tot;
} inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} inline void chkMax(int &x, int y) {
if(y > x) x = y;
} inline int min(int x, int y) {
return x > y ? y : x;
} inline void swap(int &x, int &y) {
int t = x; x = y; y = t;
} inline void solve(int x, int y) {
int cnt = ;
for(int tmp = y; tmp != x; tmp = fa[tmp])
g[++cnt] = f[tmp];
g[++cnt] = f[x];
for(int i = ; i <= cnt / ; i++)
swap(g[i], g[cnt - i + ]); /* int cnt = dep[y] - dep[x] + 1;
for(int tmp = y; tmp != x; tmp = fa[tmp])
g[cnt--] = f[tmp];
g[cnt] = f[x];
cnt = dep[y] - dep[x] + 1; */
for(int i = ; i < cnt; i++) g[i + cnt] = g[i]; int l = , r = ;
for(int i = ; i < * cnt; i++) {
for(; l <= r && i - q[l] > (cnt / ); ++l);
if(l <= r) chkMax(ans, g[i] + g[q[l]] + i - q[l]);
for(; l <= r && g[q[r]] - q[r] <= g[i] - i; --r);
q[++r] = i;
} for(int i = ; i <= cnt; i++)
chkMax(f[x], g[i] + min(i - , cnt - (i - )));
} void dfs(int x, int fat, int depth) {
fa[x] = fat, dep[x] = depth;
low[x] = dfn[x] = ++dfsc;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
if(!dfn[y]) {
dfs(y, x, depth + );
low[x] = min(low[x], low[y]);
} else low[x] = min(low[x], dfn[y]); if(low[y] > dfn[x]) {
chkMax(ans, f[x] + f[y] + );
chkMax(f[x], f[y] + );
}
} for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
// if(y == fat) continue;
if(fa[y] != x && dfn[y] > dfn[x]) solve(x, y);
}
} int main() {
read(n), read(m);
for(int k, i = ; i <= m; i++) {
read(k);
for(int lst, now, j = ; j <= k; j++) {
read(now);
if(j != ) add(now, lst), add(lst, now);
lst = now;
}
} dfs(, , ); printf("%d\n", ans);
return ;
}

Luogu 4244 [SHOI2008]仙人掌图的更多相关文章

  1. [SHOI2008]仙人掌图

    [SHOI2008]仙人掌图 LG传送门 还不会仙人掌的同学可以看看我对仙人掌知识的一些梳理. 题意就是求仙人掌的直径,直径定义为图中最短路径最长的两点间的最短路径长度. 按照套路,先考虑求树的直径我 ...

  2. 洛谷 P4244 [SHOI2008]仙人掌图 II 解题报告

    P4244 [SHOI2008]仙人掌图 II 题目背景 题目这个II是和SHOI2006的仙人掌图区分的,bzoj没有. 但是实际上还是和bzoj1023是一个题目的. 题目描述 如果某个无向连通图 ...

  3. SHOI2008仙人掌图(tarjan+dp)

    Solution 好题啊没的说. 本题需要求出仙人掌的直径,但仙人掌是一个带有简单环的一张图无法直接用树形dp求解,但它有一个好东西就是没有类似环套环的东西,所以我们在处理时就方便了一些. 思路:ta ...

  4. BZOJ1023:[SHOI2008]仙人掌图——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=1023 Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple ...

  5. [SHOI2008]仙人掌图 II——树形dp与环形处理

    题意: 给定一个仙人掌,边权为1 距离定义为两个点之间的最短路径 直径定义为距离最远的两个点的距离 求仙人掌直径 题解: 类比树形dp求直径. f[i]表示i向下最多多长 处理链的话,直接dp即可. ...

  6. BZOJ1023 SHOI2008 仙人掌图 仙人掌、单调队列

    传送门 求仙人掌的直径,可以由求树的直径进行拓展,只需要在环上特殊判断. 沿用求树的直径的DP,对于一条不在任何环内的边,直接像树的直径一样转移,然后考虑环的影响. 设环长为\(cir\),在\(df ...

  7. P4244 [SHOI2008]仙人掌图 II

    传送门 仙人掌直径,以前好像模拟赛的时候做到过一道基环树的直径,打了个很麻烦的然而还错了--今天才发现那就是这个的弱化版啊-- 如果是树的话用普通的dp即可,记\(f[u]\)表示\(u\)往下最长能 ...

  8. 洛谷P4244 [SHOI2008]仙人掌图 II

    传送门 首先不考虑带环的仙人掌,如果只是一棵普通的树,可以通过dp求每棵子树中的最长链和次长链求树的直径. 那么如果dfs的时候遇到了环,应该用环上的两点挂着的最长链加上两点间的距离来更新树的直径,并 ...

  9. 【题解】SHOI2008仙人掌图

    本质上还是树形dp.建立圆方树,遇到圆点的时候直接求(和树形dp一样即可),遇到方点做中转点的时候要考虑会从圆的另一侧通过(需满足最短路径的原则).原本是对于圆上的点进行 \(n^{2}\) 的匹配, ...

随机推荐

  1. C#动态执行代码

          在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assem ...

  2. linux基础命令复习

    1.ls 查看文件和文件夹 1).ls -a 查看文件和文件夹,包括隐藏的 2).ls  -l 查看文件和文件夹详情 3).ls  -lh   查看文件和文件夹详情,自动生成文件大小单位 4).ls ...

  3. ADO连接ACCESS数据库

    首先在StdAfx.h中加入 建立连接:(在xxApp文件中) 1  声明变量 2 建立连接 (1) AfxOleInit 初始化 OLE 为应用程序的支持. BOOL AFXAPI AfxOleIn ...

  4. 蓝桥杯 算法训练 ALGO-36 传纸条

    算法训练 传纸条   时间限制:1.0s   内存限制:512.0MB 问题描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而 ...

  5. selenium定位方法

  6. 如何判断PHP数组是否为空

    PHP判断数组为空首选方法:count($arr),size($arr); $arr=array("");echocount($arr);echo size($arr); //输出 ...

  7. 【转】Context.getExternalFilesDir()和Context.getExternalCacheDir()方法

    应用程序在运行的过程中如果需要向手机上保存数据,一般是把数据保存在SDcard中的.大部分应用是直接在SDCard的根目录下创建一个文件夹,然后把数据保存在该文件夹中.这样当该应用被卸载后,这些数据还 ...

  8. dd命令的高级应用

    dd是Linux上的一个常用的命令.例如:dd if=/1.txt of=/tmp/2.txt     (其中, if代表input file:of代表output file, 命令的结果就是将根目录 ...

  9. hadoop中unhealthynodes的问题解决

    在yarn-site.xml中加入如下配置 <property>     <name>yarn.nodemanager.disk-health-checker.min-heal ...

  10. **python中列表 元组 字典 集合

    列表 元组 字典 集合的区别是python面试中最常见的一个问题.这个问题虽然很基础,但确实能反映出面试者的基础水平. 1.列表 列表是以方括号“[]”包围的数据集合,不同成员以“,”分隔. 列表的特 ...