【BZOJ 1023】【SHOI 2008】cactus仙人掌图

良心的题解↓

http://z55250825.blog.163.com/blog/static/150230809201412793151890/

tarjan的时候如果是树边则做树形DP(遇到环就无视),最后在tarjan回溯前扫一遍当前点为“最高点”的环,进行环上DP,这个环上DP是$O(n^2)$的,但如果我们用单调队列优化则是$O(n×2)$的

总复杂度$O(n)$真是无限仰膜OTZ

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 500003;
void read(int &k) {
	k = 0; int fh = 1; char c = getchar();
	for(; c < ‘0‘ || c > ‘9‘; c = getchar())
		if (c == ‘-‘) fh = -1;
	for(; c >= ‘0‘ && c <= ‘9‘; c = getchar())
		k = (k << 1) + (k << 3) + c - ‘0‘;
	k = k * fh;
}

struct node {int nxt, to;} E[N << 1];
int ans = 0, point[N], n, m, cnt = 0, l, r;
int deep[N], F[N], fa[N], DFN[N], low[N], Q[N << 1], tb[N << 1];

void ins(int x, int y) {E[++cnt] = (node) {point[x], y}; point[x] = cnt;}

void __(int rt, int x) {
	int tot = deep[x] - deep[rt] + 1, num = tot << 1, top = tot >> 1;
	for(int tmp = x; tmp != rt; tmp = fa[tmp]) tb[tot--] = F[tmp];
	tb[1] = F[rt];
	tot = deep[x] - deep[rt] + 1;
	for(int i = 1; i <= tot; ++i) tb[tot + i] = tb[i];
	l = r = 1; Q[1] = 1;
	for(int i = 2; i <= num; ++i) {
		while (l <= r && i - Q[l] > top) ++l;
		ans = max(ans, tb[i] + i + tb[Q[l]] - Q[l]);
		while (l <= r && tb[Q[r]] - Q[r] <= tb[i] - i) --r;
		Q[++r] = i;
	}
	for(int i = 2; i <= tot; ++i)
		F[rt] = max(F[rt], tb[i] + min(i - 1, tot - i + 1));
}

void _(int x) {
	DFN[x] = low[x] = ++cnt;
	for(int tmp = point[x]; tmp; tmp = E[tmp].nxt) {
		int v = E[tmp].to;
		if (v == fa[x]) continue;
		if (!DFN[v]) {
			fa[v] = x; deep[v] = deep[x] + 1;
			_(v); low[x] = min(low[x], low[v]);
		} else
			low[x] = min(low[x], DFN[v]);
		if (DFN[x] < low[v]) {
			ans = max(ans, F[x] + F[v] + 1);
			F[x] = max(F[x], F[v] + 1);
		}
	}
	for(int tmp = point[x]; tmp; tmp = E[tmp].nxt) {
		int v = E[tmp].to;
		if (x == fa[v] || DFN[x] > DFN[v]) continue;
		__(x, v);
	}
}

int main() {
	read(n); read(m);

	int u, v, k;
	for(int i = 1; i <= m; ++i) {
		read(k); read(u);
		for(--k; k; --k) {read(v); ins(u, v); ins(v, u); u = v;}
	}

	cnt = 0;
	_(1);
	printf("%d\n", ans);
	return 0;
}

仙人掌虽然偏,但是不知道也不可以,这个代码是我磕了一晚上题解的成果QAQ

时间: 2024-10-02 02:59:39

【BZOJ 1023】【SHOI 2008】cactus仙人掌图的相关文章

【BZOJ 1023】 [SHOI2008]cactus仙人掌图

1023: [SHOI2008]cactus仙人掌图 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 1235  Solved: 482 [Submit][Status] Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是--注意到它有三条简单

bzoj 1023: [SHOI2008]cactus仙人掌图 tarjan索环&amp;&amp;环上单调队列

1023: [SHOI2008]cactus仙人掌图 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1141  Solved: 435[Submit][Status] Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路

【BZOJ】【1023】【SHOI2008】cactus仙人掌图

DP/仙人掌 题解:http://hzwer.com/4645.html->http://z55250825.blog.163.com/blog/static/150230809201412793151890/ QAQ了 呃……第一次做仙人掌的题目……感觉性质还是蛮神奇的(我是不是应该先做一点环套树的题目呢?>_>) 每个点都只会在一个简单环上,所以在dfs的时候,对于一个环,它上面的点是深度连续的一段(沿着father可以遍历这个环!),然后最后一个点再指回起始点,所以只要low改变了

bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与x在同一个环上,环形DP,单调队列优化 对于每一个环,深度最小的那个点 有可能会更新 上层节点, 所以 每一个环DP完之后,更新 dp[深度最小的点] #include<cstdio> #include<iostream> #include<algorithm> using

[树形dp][Tarjan][单调队列] Bzoj 1023 cactus仙人掌图

Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌 图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(4,3,2,1,6 ,5,4).(7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同时出现在前两 个的简单回路里.另外,第三张图也不是仙人图,因为它并不是连通图

HDU - 3594 Cactus(仙人掌图)

题目大意:给出仙人掌图的定义: 1.必须是强连通 2.每条边只能属于一个环 解题思路:在tarjan算法中加入点东西就可以判断了 只要该点能连到之前的点,那么形成环了,找到这个环的所有的边,并标记 如果有一条边被标记了两次了,那图就不是仙人掌图了 关键是怎么找到这个环的所有边,我们可以引入另一个栈,这个栈存放的是边的序号 假设当前点为u,u点连回之前的点是v,那么就从栈里面找边,找到出发点为v的边为止,找到的这些边都是环上的边,这个和tarjan算法的找同一个连通分量的点的道理是一样 #incl

bzoj 1023: [SHOI2008]cactus仙人掌图 2125: 最短路 4728: 挪威的森林 静态仙人掌上路径长度的维护系列

%%% http://immortalco.blog.uoj.ac/blog/1955 一个通用的写法是建树,对每个环建一个新点,去掉环上的边,原先环上每个点到新点连边,边权为点到环根的最短/长路长度 1023 求仙人掌直径 树形dp,维护每个点向下的最长和次长路径长度,对原有的点直接更新答案,对新点可以把对应环上的点取出,倍长,破环成链,并用单调队列正反各扫一次 #include<cstdio> char buf[5000000],*ptr=buf-1; int _(){ int x=0,c

bzoj 1023 [SHOI2008]cactus仙人掌图 ( poj 3567 Cactus Reloaded )——仙人掌直径模板

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1023 http://poj.org/problem?id=3567 因为lyd在讲课,所以有了lyd的模板.感觉人家写得好好呀!于是学习(抄)了一下.可以记一记. 反正各种优美.那个dp断环成链的地方那么流畅自然!tarjan里的那些 if 条件那么美! 不过十分不明白为什么边要开成4倍的.开成2倍的真的会RE.怎么分析仙人掌的边数? #include<iostream> #includ

BZOJ.1023.[SHOI2008]cactus仙人掌图(DP)

题目链接 类似求树的直径,可以用(类似)树形DP求每个点其子树(在仙人掌上就是诱导子图)最长链.次长链,用每个点子节点不同子树的 max{最长链}+max{次长链} 更新答案.(不需要存次长链,求解过程中先更新ans,然后再更新最长链即可) 设f[i]为点i的诱导子图中最长链的长度. 对于环,我们找一个环上dep[]最小的点x代表这个环 看做一个点(dep为按DFS顺序更新的),求出f[x],环以外的部分像树一样直接做就可以. 对于环的处理:f[x]比较显然,f[x]=max{f[v]+dis(