bzoj1023: [SHOI2008]cactus仙人掌图
题目就是算法啊
求仙人掌图的直径也算是基本问题之一了吧。
简单的想法就是dp,当y是割点,那么直接更新答案没有问题,也就是常规的treeDP求树的直径
但是呢当构环了情况就会有些复杂。
需要用到单调队列维护环里面的ans,不过f还是可以直接继承的。
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int n,m; struct node { int x,y,next; }a[2100000];int len,last[51000]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int f[51000],ans; int d[110000],list[110000]; int fa[51000],dep[51000]; void DP(int rt,int bac) { int tot=dep[bac]-dep[rt]+1; for(int i=tot;i>=1;i--) d[i]=d[i+tot]=f[bac], bac=fa[bac]; int head=1,tail=1;list[1]=1; for(int i=2;i<=tot*2;i++) { while(head<=tail&&i-list[head]>tot/2)head++; ans=max(ans,d[i]+d[list[head]]+i-list[head]); while(head<=tail&&d[list[tail]]-list[tail]<=d[i]-i)tail--; list[++tail]=i; } for(int i=1;i<=tot;i++) f[rt]=max(f[rt],d[i]+min(i-1,tot-i+1)); } int z,dfn[51000],low[51000]; void cactus(int x) { dfn[x]=low[x]=++z; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(dfn[y]==0) { fa[y]=x; dep[y]=dep[x]+1; cactus(y); low[x]=min(low[x],low[y]); if(dfn[x]<low[y]) { ans=max(ans,f[x]+f[y]+1); f[x]=max(f[x],f[y]+1); } } else if(y!=fa[x]) low[x]=min(low[x],dfn[y]); } for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(fa[y]!=x&&dfn[x]<dfn[y])DP(x,y); } } int main() { int T,x,y; scanf("%d%d",&n,&m); len=0;memset(last,0,sizeof(last)); for(int i=1;i<=m;i++) { scanf("%d",&T); scanf("%d",&x);T--; while(T--) { scanf("%d",&y); ins(x,y);ins(y,x); x=y; } } z=0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(fa,0,sizeof(fa)); memset(dep,0,sizeof(dep)); memset(f,0,sizeof(f)); ans=0;cactus(1); printf("%d\n",ans); return 0; }
pain and happy in the cruel world.