【BZOJ】【1023】【SHOI2008】cactus仙人掌图
DP+单调队列/仙人掌
题解:http://hzwer.com/4645.html->http://z55250825.blog.163.com/blog/static/150230809201412793151890/
QAQ了
呃……第一次做仙人掌的题目……感觉性质还是蛮神奇的(我是不是应该先做一点环套树的题目呢?>_>)
每个点都只会在一个简单环上,所以在dfs的时候,对于一个环,它上面的点是深度连续的一段(沿着father可以遍历这个环!),然后最后一个点再指回起始点,所以只要low改变了,就找到了一个环。。。
判是否在环上也很好判,看下low就可以了
简单了解了仙人掌的情况,我们来看下这道题怎么做:
如果是一棵树的话,我们只要Tree DP就可以了……令f[i]表示以 i 为根的最长的链,子树合并的时候随便搞搞……比较简单。
现在麻烦的是出现了环= =所以f[i]的定义本身就有些纠结……
既然这样,那么我们就把问题分成两部分来做吧!树的部分和环的部分!
对于树的部分……同上
那么环的部分是什么情况呢?我们可以令f[i]表示环上这个点向外的最长链的长度(这个定义对于树的部分不冲突,所以不用分开算)然后环上的最长链就是:$ans=max\{ f[i]+f[j]+min\{j-i,tot-j+i\} \}$呃……其实意思就是找环上两条链出来,然后加上它俩直接的距离求个最大值。最后需要更新一下环的“根”节点的f[root],这时它的子树已经完全计算完毕了,那么f[root]就应该表示以它为根的最长链,那么我们就可以用环上其他点连出去的链来更新这个f[root](其实之前算环上两条链的时候有一种情况算不到,现在就是在做这个哦,这句如果不理解就忽视掉吧,不是很重要……)
1 /************************************************************** 2 Problem: 1023 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:212 ms 7 Memory:9792 kb 8 ****************************************************************/ 9 10 //BZOJ 1023 11 #include<cstdio> 12 #include<cstring> 13 #include<cstdlib> 14 #include<iostream> 15 #include<algorithm> 16 #define rep(i,n) for(int i=0;i<n;++i) 17 #define F(i,j,n) for(int i=j;i<=n;++i) 18 #define D(i,j,n) for(int i=j;i>=n;--i) 19 using namespace std; 20 typedef long long LL; 21 inline int getint(){ 22 int v=0,r=1; char ch=getchar(); 23 for(;!isdigit(ch);ch=getchar()) if(ch=='-')r=-1; 24 for(; isdigit(ch);ch=getchar()) v=v*10+ch-'0'; 25 return v*r; 26 } 27 const int N=1e5+10,INF=~0u>>2; 28 /*********************template******************/ 29 int to[N<<2],next[N<<2],head[N],cnt; 30 void add(int x,int y){ 31 to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt; 32 to[++cnt]=x; next[cnt]=head[y]; head[y]=cnt; 33 } 34 int dfn[N],low[N],fa[N],dep[N],dfs_clock,a[N],Q[N],f[N]; 35 int n,m,ans; 36 void dp(int root,int x){ 37 int tot=dep[x]-dep[root]+1,j=tot; 38 for(int i=x;i!=root;i=fa[i]) 39 a[j--]=f[i]; 40 a[j]=f[root]; 41 F(i,1,tot) a[i+tot]=a[i]; 42 int l=0,r=-1; 43 Q[++r]=1; 44 F(i,2,tot*2){ 45 while(l<=r && i-Q[l]>tot/2) l++; 46 ans=max(ans,a[i]+i+a[Q[l]]-Q[l]); 47 while(l<=r && a[Q[r]]-Q[r]<a[i]-i) r--; 48 Q[++r]=i; 49 } 50 F(i,2,tot) 51 f[root]=max(f[root],a[i]+min(i-1,tot-i+1)); 52 } 53 void dfs(int x){ 54 dfn[x]=low[x]=++dfs_clock; 55 for(int i=head[x];i;i=next[i]) 56 if (to[i]!=fa[x]){ 57 if (!dfn[to[i]]){ 58 fa[to[i]]=x; 59 dep[to[i]]=dep[x]+1; 60 dfs(to[i]); 61 low[x]=min(low[x],low[to[i]]); 62 }else low[x]=min(low[x],dfn[to[i]]); 63 if (dfn[x]<low[to[i]]){ 64 ans=max(ans,f[x]+f[to[i]]+1); 65 f[x]=max(f[x],f[to[i]]+1); 66 } 67 } 68 for(int i=head[x];i;i=next[i]) 69 if (fa[to[i]]!=x && dfn[x]<dfn[to[i]]) 70 dp(x,to[i]); 71 } 72 int main(){ 73 #ifndef ONLINE_JUDGE 74 freopen("1023.in","r",stdin); 75 freopen("std.out","w",stdout); 76 #endif 77 n=getint(); m=getint(); 78 F(i,1,m){ 79 int k=getint(),a=getint(),b; 80 F(i,2,k){ 81 b=getint(); 82 add(a,b); 83 a=b; 84 } 85 } 86 dfs(1); 87 printf("%d\n",ans); 88 return 0; 89 }
1023: [SHOI2008]cactus仙人掌图
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 1329 Solved: 532
[Submit][Status][Discuss]
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,你的任务是求 出给定的仙人图的直径。
Input
输入的第一行包括两个整数n 和m(1≤n≤50000以及0≤m≤10000)。其中n代表顶点个数,我们约定图中的顶点将从1到n编号。接下来一共有m行。代表m条路径。每行的开 始有一个整数k(2≤k≤1000),代表在这条路径上的顶点个数。接下来是k个1到n之间的整数,分别对应了一个顶点,相邻的顶点表示存在一条连接这两 个顶点的边。一条路径上可能通过一个顶点好几次,比如对于第一个样例,第一条路径从3经过8,又从8返回到了3,但是我们保证所有的边都会出现在某条路径 上,而且不会重复出现在两条路径上,或者在一条路径上出现两次。
Output
只需输出一个数,这个数表示仙人图的直径长度。
Sample Input
9 1 2 3 4 5 6 7 8 3
7 2 9 10 11 12 13 10
5 2 14 9 15 10 8
10 1
10 1 2 3 4 5 6 7 8 9 10
Sample Output
HINT
对第一个样例的说明:如图,6号点和12号点的最短路径长度为8,所以这张图的直径为8。
【注意】使用Pascal语言的选手请注意:你的程序在处理大数据的时候可能会出现栈溢出。如果需要调整栈空间的大小,可以在程序的开头填加一句:{$M 5000000},其中5000000即指代栈空间的大小,请根据自己的程序选择适当的数值。