题解:P4244 [SHOI2008] 仙人掌图 II
题意
本文同步自洛谷专栏,题目传送门。
求一棵仙人掌的直径。
分析
首先利用 tarjan 求点双,构建出一棵圆方树,要求圆点、方点相间。建方点时,将当前 dfs 的 \(u\) 作为该方点的父亲,其余的点作为儿子。
在这棵圆方树上 dp 求解。
如果当前点为圆点 \(u\),像一般的树上 dp 求直径一样,记录 \(fi,se\) 表示最大值和次大值,更新一下答案,并记录下 \(mx_u=fi\) 以供方点使用。
如果为方点 \(u\),记 \(half\) 为环长一半(下取整),先求一遍 \(u\) 子树内的点到 \(u\) 的父亲的距离的最大值,以返回给圆点。
然后再考虑该点双内的情况,此时情况类似基环树,首先倍长环,断环为链。
然后每一个点有点权 \(mx_i\),求 \(\max_{j<i\land i-j\le half}(mx_i+mx_j+i-j)\)。
前一个限制在顺序扫描过程中容易实现,后一个限制形如滑动窗口,使用单调队列维护满足条件的最大的 \(mx_i-i\) 即可。
时间复杂度线性。
实现
实现上,个人采用的是圆点方点分别写一个 dfs 函数,可能更清晰一些。
$\red{\text{code}}$
#include<bits/stdc++.h>
using namespace std;
#define gc() (rp1==rp2&&(rp2=(rp1=buf)+fread(buf,1,IO,stdin)),*rp1++)
#define N 100005
const int IO=1<<22;
char buf[IO+1],*rp1,*rp2;
int n,m,scc,fa[N],stk[N<<1];//fa 方点的圆点父亲
int dfn[N],low[N],tot,tp;
int siz[N],mx[N],res,Q[N];//单调队列
vector<int>E[N],G[N];//G:方点信息
inline int read(){
int a=0,c=gc();
while(!isdigit(c)) c=gc();
while(isdigit(c)) a=10*a+c-'0',c=gc();
return a;
}
void tarjan(int u,int pre){
dfn[u]=low[u]=++tot,stk[++tp]=u;
for(auto &v:E[u]){
if(!dfn[v]){
tarjan(v,u),low[u]=min(low[v],low[u]);
if(low[v]<dfn[u]) continue;
scc++,fa[scc]=u;//父亲可以在链头或链尾
do{
G[scc].emplace_back(stk[tp--]);
}while(stk[tp+1]!=v);//特别注意此处
//不要误写为 while(stk[tp]!=u) ……
siz[scc]=G[scc].size();//环长-1
}
else if(v!=pre) low[u]=min(low[u],dfn[v]);
}
}
inline void cmax(int &a,int b){(a<b)&&(a=b);}
void dfs1(int u);
int dfs2(int u);
void dfs1(int u){//对圆点 dp
int fi=0,se=0,tmp;
for(auto &v:E[u]){
tmp=dfs2(v);//先遍历所有方点
if(tmp>fi) swap(fi,tmp);
if(tmp>se) swap(se,tmp);
}
cmax(res,se+fi),mx[u]=fi;
}
int dfs2(int u){//对方点 dp
for(auto &v:G[u]) dfs1(v);
tp=0;//建议在这里清空,比较清晰,以防出错
for(auto &v:G[u]) stk[++tp]=mx[v];//复用了 stk 数组
int half=(siz[u]+1)>>1,ret=0;//环长一半下取整
for(int i=1;i<=half;i++) cmax(ret,i+stk[i]);//算出到根距离
for(int i=half+1;i<=siz[u];i++) cmax(ret,siz[u]-i+1+stk[i]);
stk[++tp]=0,Q[1]=1;//倍长环
for(auto &v:G[u]) stk[++tp]=mx[v];
for(int i=2,h=1,t=2;i<=tp;i++){//单调队列,限长 half,[h,t)
while(h!=t&&Q[h]<i-half) h++;
cmax(res,stk[i]+stk[Q[h]]+i-Q[h]);
while(h!=t&&stk[Q[t-1]]-Q[t-1]<=stk[i]-i) t--;
Q[t++]=i;
}
return ret;
}
int main(){
n=read(),m=read();
for(int i=1,k,x,y;i<=m;i++){
k=read(),x=read();
for(int j=2;j<=k;j++){
y=read(),E[x].emplace_back(y);
E[y].emplace_back(x),x=y;
}
}
tarjan(1,0);//构建圆方树
for(int i=1;i<=n;i++) E[i].clear();
for(int i=1;i<=scc;i++) E[fa[i]].emplace_back(i);
tp=0,dfs1(1),printf("%d",res);
return 0;
}
洛谷账号 https://www.luogu.com.cn/user/1415227

浙公网安备 33010602011771号