【题解】SHOI2008仙人掌图
本质上还是树形dp。建立圆方树,遇到圆点的时候直接求(和树形dp一样即可),遇到方点做中转点的时候要考虑会从圆的另一侧通过(需满足最短路径的原则)。原本是对于圆上的点进行 \(n^{2}\) 的匹配,果断超时。但没有发现 \(n ^ {2}\) 的dp明显是一个可以单调队列优化的dp。所以在遇上难解决的问题的时候,一定要融会贯通地思考。有一个细节:将圆复制一下可以去掉 \(max\)造成的影响,dp就十分方便了。
#include <bits/stdc++.h>
using namespace std;
#define maxn 500000
int n, m, N, timer, ans;
int fa[maxn], dfn[maxn], low[maxn];
int f[maxn][], id[maxn], S[maxn];
int tmp[maxn], q[maxn];
int F[maxn][], SS[maxn]; struct edge
{
int cnp, head[maxn], to[maxn], last[maxn], w[maxn];
edge() { cnp = ; }
void add(int u, int v, int ww)
{
to[cnp] = v, last[cnp] = head[u], w[cnp] = ww, head[u] = cnp ++;
to[cnp] = u, last[cnp] = head[v], w[cnp] = ww, head[v] = cnp ++;
}
}E1, E2; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} void Solve(int u, int v)
{
++ N; int ID = , pre = ;
for(int i = v; i != fa[u]; i = fa[i])
S[i] = pre ++, id[i] = ++ ID;
S[N] = S[u], S[u] = ;
for(int i = v; i != fa[u]; i = fa[i])
E2.add(N, i, min(S[i], S[N] - S[i]));
} void Tarjan(int u)
{
dfn[u] = low[u] = ++ timer;
for(int i = E1.head[u]; i; i = E1.last[i])
{
int v = E1.to[i];
if(v == fa[u]) continue;
if(!dfn[v])
{
fa[v] = u; Tarjan(v);
low[u] = min(low[u], low[v]);
}
else low[u] = min(low[u], dfn[v]);
if(low[v] > dfn[u]) E2.add(u, v, );
}
for(int i = E1.head[u]; i; i = E1.last[i])
{
int v = E1.to[i];
if(dfn[u] < dfn[v] && fa[v] != u) Solve(u, v);
}
} void update(int u, int w)
{
if(w <= f[u][]) return;
if(w < f[u][]) f[u][] = w;
else f[u][] = f[u][], f[u][] = w;
} bool cmp(int a, int b) { return id[a] < id[b]; } int Work(int u)
{
int head = , tail = , cnt = , ans = , len = ;
for(int i = E2.head[u]; i; i = E2.last[i])
tmp[++ cnt] = f[E2.to[i]][], len ++;
for(int i = ; i <= len; ++ i)
tmp[++ cnt] = tmp[i];
q[head] = ;
for(int i = ; i <= cnt; ++ i)
{
while(head < tail && i - q[head] > len / ) ++ head;
ans = max(ans, tmp[i] + tmp[q[head]] + i - q[head]);
while(head <= tail && tmp[i] - i > tmp[q[tail]] - q[tail] ) -- tail;
q[++ tail] = i;
}
return ans;
} void dfs(int u, int ff)
{
for(int i = E2.head[u]; i; i = E2.last[i])
{
int v = E2.to[i];
if(v == ff) continue; else dfs(v, u);
update(u, f[v][] + E2.w[i]);
}
if(u <= n) ans = max(ans, f[u][] + f[u][]);
else ans = max(ans, Work(u));
} int main()
{
N = n = read(), m = read();
for(int i = ; i <= m; i ++)
{
int tot = read(), u, pre;
for(int j = ; j <= tot; j ++)
{
u = read(); if(j == ) { pre = u; continue; }
E1.add(u, pre, ); pre = u;
}
}
Tarjan();
dfs(, );
printf("%d\n", ans);
return ;
}
【题解】SHOI2008仙人掌图的更多相关文章
- [SHOI2008]仙人掌图
[SHOI2008]仙人掌图 LG传送门 还不会仙人掌的同学可以看看我对仙人掌知识的一些梳理. 题意就是求仙人掌的直径,直径定义为图中最短路径最长的两点间的最短路径长度. 按照套路,先考虑求树的直径我 ...
- 洛谷 P4244 [SHOI2008]仙人掌图 II 解题报告
P4244 [SHOI2008]仙人掌图 II 题目背景 题目这个II是和SHOI2006的仙人掌图区分的,bzoj没有. 但是实际上还是和bzoj1023是一个题目的. 题目描述 如果某个无向连通图 ...
- BZOJ1023:[SHOI2008]仙人掌图——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=1023 Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple ...
- SHOI2008仙人掌图(tarjan+dp)
Solution 好题啊没的说. 本题需要求出仙人掌的直径,但仙人掌是一个带有简单环的一张图无法直接用树形dp求解,但它有一个好东西就是没有类似环套环的东西,所以我们在处理时就方便了一些. 思路:ta ...
- [SHOI2008]仙人掌图 II——树形dp与环形处理
题意: 给定一个仙人掌,边权为1 距离定义为两个点之间的最短路径 直径定义为距离最远的两个点的距离 求仙人掌直径 题解: 类比树形dp求直径. f[i]表示i向下最多多长 处理链的话,直接dp即可. ...
- BZOJ1023 SHOI2008 仙人掌图 仙人掌、单调队列
传送门 求仙人掌的直径,可以由求树的直径进行拓展,只需要在环上特殊判断. 沿用求树的直径的DP,对于一条不在任何环内的边,直接像树的直径一样转移,然后考虑环的影响. 设环长为\(cir\),在\(df ...
- Luogu 4244 [SHOI2008]仙人掌图
BZOJ 1023 如果我们把所有的环都缩成一个点,那么整张图就变成了一棵树,我们可以直接$dp$算出树的直径. 设$f_x$表示$x$的子树中最长链的长度,那么对于$x$的每一个儿子$y$,先用$f ...
- P4244 [SHOI2008]仙人掌图 II
传送门 仙人掌直径,以前好像模拟赛的时候做到过一道基环树的直径,打了个很麻烦的然而还错了--今天才发现那就是这个的弱化版啊-- 如果是树的话用普通的dp即可,记\(f[u]\)表示\(u\)往下最长能 ...
- 洛谷P4244 [SHOI2008]仙人掌图 II
传送门 首先不考虑带环的仙人掌,如果只是一棵普通的树,可以通过dp求每棵子树中的最长链和次长链求树的直径. 那么如果dfs的时候遇到了环,应该用环上的两点挂着的最长链加上两点间的距离来更新树的直径,并 ...
随机推荐
- js判断两个日期是否在几个月之内
//比较两个时间 time1,time2均为日期类型 //判断两个时间段是否相差 m 个月 function completeDate(time1 , time2 , m) { var diffyea ...
- ethereum Pet Shop
在部署宠物商店时遇到的一些坑,给大家总结一下,以免大家多走弯路 转载的地址(详细):https://steemit.com/cn/@lucia3/ethereum-pet-shop 启动"n ...
- __name__ 和 "__main__"
本模块名: person 调用者模块名: start import sys def funcperson(): print('我是人') print(sys.modules[__name__]) # ...
- 强化记忆之php
php 输出的区分 新手摸索道路,有说不对的地方,还请多多包涵. echo 能够输出一个以上的字符串,也能输出html标签 print 一次只能接受一个字符串(区分与echo),也能输出html标签 ...
- Flask初学者:url_for
URL反转:反转是指通过视图函数名称得到其对应的URL(有反转也就有正转,即通过URL得到视图函数返回的内容,也就是我们平时的访问网页了),需要“url_for(endpoint, **values) ...
- PAT-B java实现
注意:java提交PAT时,不需要加package : 类名必须是Main. 1001 害死人不偿命的(3n+1)猜想 (15) 输入格式:每个测试输入包含1个测试用例,即给出自然数n的值. 输出格式 ...
- 小程序开发-7-访问api数据与ES6在小程序中的应用
访问API数据与ES6在小程序中的应用 看待组件的两种观点 组件复用 代码分离-(特别重要) 不能在一个页面写所有的代码,代码分离具有很强的可读性.可维护性 Blink Api 介绍与测试API ur ...
- Hibernate-ORM:04.Hibernate中的get()和load()
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客会讲如何用get()或load()查询单个对象和对缓存的简单操作,以及他俩的区别和相同(前面有的那些配 ...
- 纯原生仿ES6的Object.assign,实现深度合并对象
源码: function isObj(x){ var type = typeof x; return x !== null && (type === 'object' || type ...
- Selenium八大元素定位方式
1.根据id来定位: import org.openqa.selenium.By;import org.openqa.selenium.WebDriver;import org.openqa.sele ...