[树形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)同时出现在前两
个的简单回路里。另外,第三张图也不是仙人图,因为它并不是连通图。显然,仙人图上的每条边,或者是这张仙
人图的桥(bridge),或者在且仅在一个简单回路里,两者必居其一。定义在图上两点之间的距离为这两点之间最
短路径的距离。定义一个图的直径为这张图相距最远的两个点的距离。现在我们假定仙人图的每条边的权值都是1
,你的任务是求出给定的仙人图的直径。

题解

  • 如果是圆圆边的话和普通的树形dp一样
  • 对于环的话,把他拎出来单独考虑,首先要计算出这个环的贡献,然后更新环的最顶点
  • 更新的话,就这个直接拿环上的点的dp值,再计算一下这两点之间的最短路,相加后更新
  • 贡献的话,就是max(f[i]+f[j]+dis(i,j)),dis(i,j)=min(abs(deep[i]-deep[j]),size[环]-abs(deep[i]-deep[j]))
  • 可以维护一个单调队列,按照深度进栈

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 using namespace std;
 5 const int N=60010;
 6 struct edge{int to,from;}e[N*8];
 7 int n,m,L,R,cnt=1,ans=1,head[N],Q[N*2],q[N*2],f[N],Fa[N],deep[N],dfn[N],low[N];
 8 void insert(int x,int y)
 9 {
10     e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt;
11     e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt;
12 }
13 void dp(int x,int y)
14 {
15     int r=0;
16     for (int i=y;i!=x;i=Fa[i]) Q[++r]=i; Q[++r]=x;
17     reverse(&Q[1],&Q[r+1]);
18     for (int i=1;i<=r;i++) Q[i+r]=Q[i];
19     L=1,R=0;
20     for (int i=1;i<=r*2;i++)
21     {
22         while (L<=R&&i-q[L]>r/2) ++L;
23         if (L<=R) ans=max(ans,f[Q[i]]+f[Q[q[L]]]+i-q[L]);
24         while (L<=R&&f[Q[i]]-i>f[Q[q[R]]]-q[R]) R--;
25         q[++R]=i;
26     }
27     for (int i=y;i!=x;i=Fa[i]) f[x]=max(f[x],f[i]+min(deep[i]-deep[x],1+deep[y]-deep[i]));
28 }
29 void dfs(int x,int fa)
30 {
31     Fa[x]=fa,dfn[x]=low[x]=++dfn[0],deep[x]=deep[fa]+1;
32     for (int i=head[x];i;i=e[i].from)
33     {
34         if (!dfn[e[i].to]) dfs(e[i].to,x),low[x]=min(low[x],low[e[i].to]); else if (e[i].to!=fa) low[x]=min(low[x],dfn[e[i].to]);
35         if (low[e[i].to]>dfn[x]) ans=max(ans,f[x]+f[e[i].to]+1),f[x]=max(f[x],f[e[i].to]+1);
36     }
37     for (int i=head[x];i;i=e[i].from) if (Fa[e[i].to]!=x&&dfn[x]<dfn[e[i].to]) dp(x,e[i].to);
38 }
39 int main()
40 {
41     scanf("%d%d",&n,&m);
42     for (int i=1,k,x,y;i<=m;i++)
43     {
44         scanf("%d%d",&k,&x);
45         for (int j=1;j<k;j++) scanf("%d",&y),insert(x,y),x=y;
46     }
47     dfs(1,0),printf("%d",ans);
48 }

原文地址:https://www.cnblogs.com/Comfortable/p/11367500.html

时间: 2024-11-12 16:45:57

[树形dp][Tarjan][单调队列] Bzoj 1023 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)

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

【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仙人掌图

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

[noi2013]快餐店 基环树dp,单调队列维护最大值和次大值

#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 220000 #define inf 0x3ffffffffffffffLL typedef long long ll; int v[N],e[N],ne[N],nn,w[N]; void add(int x,int y,int z){ ne[++nn

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

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】【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&

HDU 4374 One hundred layer DP的单调队列优化

One hundred layer Problem Description Now there is a game called the new man down 100th floor. The rules of this game is: 1.  At first you are at the 1st floor. And the floor moves up. Of course you can choose which part you will stay in the first ti