bzoj 1023: [SHOI2008]cactus仙人掌图
2017-08-08 15:52
453 查看
题意:给一颗仙人掌,求它的直径。
有关的定义题目中说的很清楚,就不再重复了。 首先假如给的是一棵树,求树的直径,就比较简单,可以dfs或bfs。
考虑dp的做法。
设集合g表示i到其各个子树的最长链链,即以i为最高点,且除端点外,没有相交的不同最长链。(语文死得早,意会下吧)
于是过i,且完全在其子树中的最长链是g中最大的+次大的。其长度记为F。
直径显然存在唯一最高点,所以ans=Max{F1,F2,F3,……Fn,}
有个不错的求法,f[i]表示i为最高点的最长链,每次更新完儿子y后用f[x]+f[y]+1更新答案,再用f[y]+1更新f[x]。拓扑序dp就行了。
根据仙人掌图的定义:同一条边至多出现在一个环中,可知若将原图缩点后(一个点是一个简单环或几个并在一起),一定是一棵树,因为若还出现环,就不符合定义了。不连通?不存在的事。
我说的一个环的最高点,是离根最近,也就是最先访问的点。他的状态可以表示整个环的状态,因为对于他的祖先来说,只有他是有用的,而他们的儿子,因为按照dfs序,已经访问完了,更没有影响。
要缩点当然要上tarjan了,但有些不同,大概是一边缩一边dp,然后用自己的状态更新父亲状态(一波蒟蒻口胡)
dfn还是表示dfs序,low可以理解为所处的环中最高点的编号,一个点就假装是环吧。
那么对于所有边可以分为两类,桥和环中的边,也就是树边和非树边(纯属蒟蒻瞎定义)
当一个点x并没有处于环中时,直接按上面说的那样dp就可以了,此时dfn[x]=low[x],且对于他所有儿子y都有low[y]大于dfn[x]。
当你发现low[y]<=dfn[x]是,说明x,y一定形成了环,这时就直接跳过不更新答案,访问别的点,f数组暂时不考虑有环的情况,最后找回环的最高点更新。
但问题是怎么找回最高点?
难以描述,直接上代码
if(tr[y].fa!=x&&dfn[x]<dfn[y])1
1
容易得知,整个环一定是按某个顺序访问的
那当回到最高点时,再访问最低点(不是直观的低,意思是dfn最大的点)时才会满足这两个条件。
注意,不能写成这样:
if(tr[y].fa!=x&&dfn[x]==low[x])1
1
如果研究过样例,可以发现这种情况
这种情况最好一个个环处理,如果传一个这么复杂的环进去,起码我是不知道怎么dp啊。
所以我们得到了最高点的和最低点,整个环由他们的fa指针形成一条链,可以很容易的提取出来,然后就是喜闻乐见的dp了。
for(int i=2;i<=tot;i++) f[root]=max(f[root],map[i]+min(i-1,tot-i+1));1
2
1
2
还有一个巨大的问题,就是没有统计经过环的边。如图:
这是其实只要在提取出对应点,做一次dp就可以了,或者都不算dp。
for(int i=1;i<=tot;i++) for(int j=i+1;j<=tot;j++) ans=max(ans,map[i]+map[j]+min(j-i,i+tot-j));1
2
3
1
2
3
正确性显然,只是会T到妈都不认得。
设dis(x,y)表示环上x,y的最短路,易证dis(x,y)<=tot/2。
所以断环为链,复制一份,显然有单调性,直接单调队列就可有O(n)了。
code:
#include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<iostream> using namespace std; const int inf=(1<<28); struct node{int y,next;}a[2000005];int last[50010],len=0; int dfn[50010],low[50010],n,m,f[50010],ans=-inf,id=0; struct trnode{ int dep,fa; }tr[50010]; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void ins(int x,int y) { a[++len].y=y; a[len].next=last[x];last[x]=len; } int map[100010],q[100010],l,r; void dp(int root,int x)//最高点和最低点 { int tot=tr[x].dep-tr[root].dep+1; for(int i=x;i!=root;i=tr[i].fa) map[tot--]=f[i]; map[tot]=f[root]; tot=tr[x].dep-tr[root].dep+1; for(int i=1;i<=tot;i++) map[i+tot]=map[i]; q[1]=1;l=1;r=1; for(int i=2;i<=tot+1;i++)//单调队列 { while(l<=r&&i-q[l]>tot/2) l++; int j=q[l]; ans=max(ans,map[i]+map[j]+i-j); while(l<=r&&map[q[r]]-q[r]<=map[i]-i) r--; q[++r]=i; } for(int i=2;i<=tot;i++)//更新最高点 f[root]=max(f[root],map[i]+min(i-1,tot-i+1)); } void dfs(int x,int fa) { dfn[x]=low[x]=++id;tr[x].fa=fa;tr[x].dep=tr[fa].dep+1; for(int i=last[x];i;i=a[i].next) { int y=a[i].y; if(y==fa) continue; if(dfn[y]==-1) { dfs(y,x); low[x]=min(low[x],low[y]); } else low[x]=min(low[x],dfn[y]); if(dfn[x]<low[y])//非环情况 { ans=max(ans,f[y]+f[x]+1); f[x]=max(f[y]+1,f[x]); } } for(int i=last[x];i;i=a[i].next) { int y=a[i].y; if(tr[y].fa!=x&&dfn[x]<dfn[y])//找最高点 dp(x,y); } } int main() { n=read();m=read(); memset(last,0,sizeof(last)); for(int i=1;i<=m;i++) { int k,x;k=read();x=read(); for(int j=2;j<=k;j++) { int y;y=read(); ins(x,y);ins(y,x);x=y; } } memset(f,0,sizeof(f)); memset(dfn,-1,sizeof(dfn)); tr[0].dep=0;dfs(1,0); printf("%d",ans); }
相关文章推荐
- BZOJ.1023.[SHOI2008]cactus仙人掌图(DP)
- BZOJ1023: [SHOI2008]cactus仙人掌图
- bzoj1023 [SHOI2008]cactus仙人掌图(仙人掌dp+单调队列)
- [仙人掌直径 单调队列 DP] BZOJ 1023 [SHOI2008]cactus仙人掌图
- bzoj1023: [SHOI2008]cactus仙人掌图
- BZOJ1023 [SHOI2008]cactus仙人掌图
- [BZOJ1023][SHOI2008][仙人掌直径][队列优化DP]cactus仙人掌图
- 【BZOJ】1023: [SHOI2008]cactus仙人掌图 静态仙人掌(DFS树)
- BZOJ1023 [SHOI2008]cactus仙人掌图
- BZOJ1023[SHOI2008]cactus仙人掌图 【仙人掌DP】
- BZOJ 1023: [SHOI2008]cactus仙人掌图(仙人掌DP)
- bzoj 1023: [SHOI2008]cactus仙人掌图
- bzoj1023 [SHOI2008]cactus仙人掌图
- bzoj1023【SHOI2008】cactus仙人掌图
- bzoj千题计划224:bzoj1023: [SHOI2008]cactus仙人掌图
- BZOJ1023: [SHOI2008]cactus仙人掌图
- bzoj1023 [SHOI2008]cactus仙人掌图
- [bzoj1023][SHOI2008]cactus仙人掌图
- 【BZOJ 1023】[SHOI2008]cactus仙人掌图
- 【BZOJ 1023】【SHOI 2008】cactus仙人掌图