题意:
求一颗仙人掌的直径。
题解:
DP。
首先建出图的DFS树。
因为是仙人掌图,所以每个环必定有一个dfs序最小的点,连接着若干条后向边和树边,表示环上的边或者割边。
记录 f[i] 表示dfs树上以 i 为根的子树(子图)中最长链。考虑DP:
1.对于一条割边, 对答案的影响为
2.对于环边先不处理,等到dfs完所有子树后重新枚举边查找环上的儿子。(因为是在dfs树上所以连接的儿子一定是环上最后一个点。)
在多个环上分别dp。
对于环的dp:
首先,只需更新dfs树中最高点的
f[i]
。因为dfs树上对前面的点有影响的只有最高点,只需用最高点储存环上信息。直接枚举所有点,
f[i]=max(f[i],maxj∈cir(i){f[j]+dis(i,j)})
。
其次,环上的两个不是最高点的点可能对答案产生影响,就像一个环上挂了许多子树,每个子树的最长链为 f[j] ,求 maxj1,j2∈cir(i){f[j1]+f[j2]+dis(i,j)} 。这是一个经典的单调队列优化dp。但是,与求基环外向树直径不同的是,这里的 dis(i,j) 取环上的最小值,所以只能从 i−(n/2)+1 开始枚举。
#include<bits/stdc++.h>
using namespace std;
streambuf *ib,*ob;
inline void init(){
ios::sync_with_stdio(false);
cin.tie(NULL);cout.tie(NULL);
ib=cin.rdbuf();ob=cout.rdbuf();
}
inline int read(){
char ch=ib->sbumpc();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=ib->sbumpc();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=ib->sbumpc();}
return i*f;
}
int buf[50];
inline void W(int x){
if(!x){ob->sputc('0');return;}
if(x<0){ob->sputc('-');x=-x;}
while(x){buf[++buf[0]]=x%10;x/=10;}
while(buf[0]){ob->sputc(buf[buf[0]--]+'0');}
}
const int Maxn=5e4+50;
int n,m,f[Maxn],dfn[Maxn],ind,low[Maxn],fa[Maxn],dep[Maxn],ans;
vector<int>edge[Maxn];
inline void dp(int rt,int x){
static int a[Maxn*2],que[Maxn*2],head,tail,cnt;
cnt=dep[x]-dep[rt]+1;
for(int i=cnt,y=x;i>=1;i--,y=fa[y])a[i]=y;
int lim=cnt/2;
memcpy(a+cnt+1,a+1,sizeof(int)*lim);
que[head=tail=1]=1;
int t=cnt;cnt+=lim;
for(int i=2;i<=cnt;i++){
while(head<=tail&&que[head]<i-lim)head++;
ans=max(ans,f[a[i]]+i+f[a[que[head]]]-que[head]);
while(head<=tail&&f[a[que[tail]]]-que[tail]<=f[a[i]]-i)tail--;
que[++tail]=i;
}
for(int i=2;i<=t;i++)
f[rt]=max(f[rt],f[a[i]]+min(i-1,t-i+1));
}
inline void dfs(int now,int father){
dep[now]=dep[father]+1;low[now]=dfn[now]=++ind;fa[now]=father;
for(int e=edge[now].size()-1;e>=0;e--)
{
int v=edge[now][e];
if(v==father)continue;
if(!dfn[v]){
dfs(v,now);
low[now]=min(low[now],low[v]);
}
else low[now]=min(low[now],dfn[v]);
if(dfn[now]<low[v]){
ans=max(ans,f[now]+f[v]+1);
f[now]=max(f[now],f[v]+1);
}
}
for(int e=edge[now].size()-1;e>=0;e--)
{
int v=edge[now][e];
if(v==father)continue;
if(fa[v]!=now&&dfn[v]>dfn[now])
dp(now,v);
}
}
int main()
{
init();n=read(),m=read();
for(int i=1;i<=m;i++)
{
int k=read(),a=read();
for(int i=2;i<=k;i++)
{
int b=read();
edge[a].push_back(b);
edge[b].push_back(a);
a=b;
}
}
dfs(1,0);
W(ans);ob->sputc('\n');
}