[SHOI2008]仙人掌图

LG传送门

还不会仙人掌的同学可以看看我对仙人掌知识的一些梳理。

题意就是求仙人掌的直径,直径定义为图中最短路径最长的两点间的最短路径长度。

按照套路,先考虑求树的直径我们是怎么做的。设\(f[i]\)表示\(i\)往下最长链的长度,\(j\)是\(i\)的儿子,转移和更新答案就是(我习惯用\(o\)表示答案):

\[f[i] = max\{f[j]\} + 1 \qquad o = max\{f[i] + f[j] + 1\}
\]

考虑放到仙人掌上,对于树边直接转移和更新答案;对于非树边,先把环拎出来,可以直接转移,设\(i\)是环顶端的点,\(j\)是环上的其他点,\(k\)是环的大小:

\[f[i] = max\{f[j] + dis(i, j)\}
\]

\(dis(i, j)\)可以用深度差和环大小减深度差的较小值来表示。

更新答案似乎不能直接搞,考虑断环为链,维护个单调队列:如果队首元素超过环大小的一半就出队,用准备进队的元素和队首元素更新答案,如果当前进队元素的\(f\)减去队尾元素的\(f\)大于等于他们在环上的距离就弹出队尾懒得放数学公式了,可以结合代码理解。

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#define R register
#define I inline
#define B 1000000
using namespace std;
const int N = 50003, M = 100003;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1 == p2) ? EOF : *p1++; }
I int rd() {
R int f = 0;
R char c = gc();
while (c < 48 || c > 57)
c = gc();
while (c > 47 && c < 58)
f = f * 10 + (c ^ 48), c = gc();
return f;
}
int s[N], p[N], d[N], l[N], e[M], q[M], f[N], t, o;
vector <int> g[N];
I int min(int x, int y) { return x < y ? x : y; }
I int max(int x, int y) { return x > y ? x : y; }
void dfs(int x, int h) {
p[x] = h, d[x] = l[x] = ++t;
R int i, y;
for (i = 0; i < s[x]; ++i) {
if (!d[y = g[x][i]])
dfs(y, x), l[x] = min(l[x], l[y]);
else
if (y ^ h)
l[x] = min(l[x], d[y]);
if (l[y] > d[x])
o = max(o, f[x] + f[y] + 1), f[x] = max(f[x], f[y] + 1);
}
for (i = 0; i < s[x]; ++i)
if (p[y = g[x][i]] ^ x && d[x] < d[y]) {
R int j, k = 0, u = 1, v = 0;
for (j = y; j ^ x; j = p[j])
e[++k] = f[j];
e[++k] = f[x], reverse(e + 1, e + k + 1);
for (j = 1; j <= k; ++j)
e[j + k] = e[j];
k <<= 1, q[++v] = 1;
for (j = 2; j <= k; q[++v] = j, ++j) {
while (j - q[u] > (k >> 2) && u <= v)
++u;
if (u <= v)
o = max(o, e[j] + e[q[u]] + j - q[u]);
while (e[j] - e[q[v]] >= j - q[v] && u <= v)
--v;
}
k >>= 1;
for (j = 2; j <= k; ++j)
f[x] = max(f[x], e[j] + min(j - 1, k - j + 1));
}
}
int main() {
R int n = rd(), m = rd(), i, j, k, x, y;
for (i = 1; i <= m; ++i) {
k = rd(), x = rd();
for (j = 1; j < k; x = y, ++j)
y = rd(), g[x].push_back(y), g[y].push_back(x);
}
for (i = 1; i <= n; ++i)
s[i] = g[i].size();
dfs(1, 0), printf("%d", o);
return 0;
}

最新文章

  1. Echarts 饼图标题文字换行问题
  2. js厘米与英寸尺码转换
  3. c#属性中的get和set属性
  4. 移动web开发—页面头部 META 总结
  5. IOS的Crash情况在Crashlytics平台上统计解决方案的一点遗憾(截止到2015年6月14日)
  6. js切换实现背景颜色
  7. 验证码的种类与实现 C#封装类 - .NET MVC WEBFORM
  8. 最全的iOS面试题及答案-转载
  9. 能源项目xml文件标签释义--DataSource
  10. Eclipse 手机开发不能运行
  11. Selenium2Library系列 keywords 之 _SelectElementKeywords 之 get_selected_list_values(self, locator)
  12. WPF的进度条progressbar,运行时间elapse time和等待spinner的实现
  13. 解决“您必须先更新GOOGLE play才能运行此应用”的问题
  14. SET ANSI_NULLS (Transact-SQL)
  15. 本学期微分方程数值解课程总结(matlab代码)
  16. cesium加载纽约市3dtiles模型
  17. 2019-oo-第二次总结
  18. 安装cx_Oracle 6
  19. Android 获取控件滑动速度,速度跟踪器VelocityTracker;
  20. R语言——实验5-聚类分析

热门文章

  1. 从零开始导入gradle项目
  2. [翻译] AnchoredFloatView
  3. [翻译] USING GIT IN XCODE [6] 在XCODE中使用GIT[6]
  4. (1)String类 (2)StringBuilder类和StringBuffer类 (3)日期相关的类
  5. codeforces 424D Biathlon Track
  6. [错误记录]python requests库 Response 判断坑
  7. CR与LF
  8. 4种Java日志管理方法
  9. JAVA反射机制教程-获取类对象
  10. Nginx总结.md