题意:给出一个仙人掌图,边权都为1,求其直径。
仙人掌图:无向图的每条边至多存在于一个简单环中。
仙人掌图直径:Max(dis(u,v)) 1<= u < v <=n。dis(u,v)是u、v之间的最短路径。
题解:
先考虑退化情况
:仙人掌退化为一棵树。很显然可以通过一个树形DP来解决,讨论路径的形态:1、分跨在一个点的两个子树中2、是一条从根节点到叶子节点的路径。每个点需要两个dp值记录子树中到叶子的最长和次长路径。然后进行简单的转移即可。
非退化情况
:对仙人掌图求出圆方树。
对于圆点
而言,处理方法和上边的一样。
对于方点
而言,问题转化为:给出一个环基树,已知环上顺次都是那些点、以及每个点向外侧延伸的最大链长。求出环基树的直径,这个问题有一种极其经典的RMQ的有限队列解法,可以做到O(环长)的复杂度。因此总体复杂度是O(n)
圆方树详细资料:WC2017课件传送门
由于本人不喜欢破环操作。。。对环基树直径采用了分类讨论的方法:环上路径是否经过环的根。也可以做到O(环长)的复杂度。
我的tarjan求点双不需要处理长度=2的无效环问题。
这个题交了十几次才AC掉…已经不省人事了…然而这只是毒瘤入门题,看来想成为毒瘤是需要付出巨大的努力…………
Code:
#include<bits/stdc++.h>
#define pb(x) push_back(x)
using namespace std;
const int maxn = 1e5+100;
vector<int>E1[maxn],ET[2*maxn],LenT[2*maxn];
int dfn[maxn],fa[2*maxn],len[maxn*2],dfs_clock;
bool inCircle[maxn];
int m,n,ans,N;
int dp[maxn][2];
int Q[maxn],head,tail,Max[maxn*2];;
inline void addEdge1(int x,int y){
E1[x].pb(y);
}
inline void addEdgeT(int x,int y,int w){
ET[x].pb(y);
LenT[x].pb(w);
}
void input(){
scanf("%d%d",&n,&m);
N=n;
for (int i=0;i<m;i++){
int k,u;
scanf("%d%d",&k,&u);
for (int j=1;j<k;j++){
int v;
scanf("%d",&v);
addEdge1(u,v);
addEdge1(v,u);
u = v;
}
}
}
void tarjan(int u){
dfn[u] = ++ dfs_clock;
for (int i=0;i<E1[u].size();i++){
int v = E1[u][i];
if (v==fa[u])continue;
if (!dfn[v]){
fa[v] =u;
tarjan(v);
}else if (dfn[v]<dfn[u]){
n++;
addEdgeT(v,n,0);
int temp = u;
len[n] = dfn[u]-dfn[v]+1;
fa[n] = v;
while (temp!=v){
inCircle[temp] = true;
addEdgeT(n,temp,min(dfn[temp]-dfn[v],len[n]-dfn[temp]+dfn[v]));
temp = fa[temp];
}
}
}
if (!inCircle[u]){
addEdgeT(fa[u],u,1);
}
dfs_clock--;
}
inline void update(int x,int w){
if (w>=dp[x][0]){
dp[x][1] = dp[x][0];
dp[x][0]= w;
}else if (w>dp[x][1]){
dp[x][1] = w;
}
}
void work(int squareU){
head = 1;tail = 0;
int length = len[squareU];
for (int i=0;i<ET[squareU].size();i++){
int v = ET[squareU][i];
while (head<=tail&&head<i+1-length/2)head++;
if (head<=tail&&head>=i+1-length/2){
ans = max(ans,dp[v][0]+i+1+Q[head]);
}
while (head<=tail&&Q[tail]<dp[v][0]-i-1)tail--;
Q[++tail] = dp[v][0]-(i+1);
}
for (int i=0;i<length/2;i++){
Max[i+1] = max(Max[i],dp[ET[squareU][i]][0]+i+1);
}
for (int i=length/2;i<ET[squareU].size();i++){
ans = max(ans,dp[ET[squareU][i]][0]+length-i-1+Max[length/2-(length-i-1)]);
}
}
void dfs(int u){
for (int i=0;i<ET[u].size();i++){
dfs(ET[u][i]);
update(u,dp[ET[u][i]][0]+LenT[u][i]);
}
if (u>N){
work(u);
}else{
ans = max(ans,dp[u][0]+dp[u][1]);
}
}
int main(){
input();
tarjan(1);
dfs(1);
cout<<ans<<endl;
return 0;
}