Description
如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌
图(cactus)。所谓简单回路就是指在图上不重复经过任何一个顶点的回路。
举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(4,3,2,1,6
,5,4)、(7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同时出现在前两个的简单回路里。另外,第三张图也不是仙人图,因为它并不是连通图。显然,仙人图上的每条边,或者是这张仙人图的桥(bridge),或者在且仅在一个简单回路里,两者必居其一。定义在图上两点之间的距离为这两点之间最短路径的距离。定义一个图的直径为这张图相距最远的两个点的距离。现在我们假定仙人图的每条边的权值都是1,你的任务是求出给定的仙人图的直径。
Input
输入的第一行包括两个整数n和m(1≤n≤50000以及0≤m≤10000)。其中n代表顶点个数,我们约定图中的顶
点将从1到n编号。接下来一共有m行。代表m条路径。每行的开始有一个整数k(2≤k≤1000),代表在这条路径上的顶点个数。接下来是k个1到n之间的整数,分别对应了一个顶点,相邻的顶点表示存在一条连接这两个顶点的边。一条路径上可能通过一个顶点好几次,比如对于第一个样例,第一条路径从3经过8,又从8返回到了3,但是我们保证所有的边都会出现在某条路径上,而且不会重复出现在两条路径上,或者在一条路径上出现两次。
Output
只需输出一个数,这个数表示仙人图的直径长度。
Sample Input
15 3
9 1 2 3 4 5 6 7 8 3
7 2 9 10 11 12 13 10
5 2 14 9 15 10
Sample Output
8
Solution
这题貌似有非圆方树解法?懒得想了。。
建立圆方树,直接(dp),记最大值和次大值。
对于圆点,贡献就直接是最大值加次大值,注意要防止他们来自同一颗子树。
对于方点,问题其实转化成了:给定一个基环树,求直径。
对于环上每个点,可以算出树上的最大值,设为(f[x])。
然后用一个单调队列维护按(f[x]-x)维护单调上升,然后细节注意下就做完了。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}
const int maxn = 1e6+10;
int n,m,cnt,head[maxn],mark[maxn],dep[maxn],f[maxn],tot=1,fr[maxn],ans;
struct edge{int to,nxt,w;}e[maxn<<1];
vector <int > r[maxn];
void add(int u,int v,int w) {e[++tot]=(edge){v,head[u],w},head[u]=tot;}
void ins(int u,int v,int w) {add(u,v,w),add(v,u,w);}
void dfs(int x,int fa) {
dep[x]=dep[fa]+1,f[x]=fa;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) {
int v=e[i].to;
if(!dep[v]) fr[v]=i,dfs(v,x);
else if(dep[v]<dep[x]) {
cnt++;ins(v,cnt,0);
int t=x;mark[i]=mark[i^1]=1;
while(t!=v) {
mark[fr[t]]=mark[fr[t]^1]=1;
r[cnt].push_back(t);
ins(t,cnt,min(dep[t]-dep[v],dep[x]-dep[t]+1));
t=f[t];
}
}
}
}
int g[maxn][2],dis[maxn],q[maxn],Head,tail,Q[maxn];
int mx[maxn];
void work(int x) {
Head=1,tail=0;int len=r[x].size()+1;int tmp=0;
for(int i=0;i<len-1;i++) {
while(Head<=tail&&q[Head]<i-len/2+1) Head++;
if(Head<=tail) tmp=max(tmp,i+g[r[x][i]][0]+Q[Head]);
while(Head<=tail&&g[r[x][i]][0]-i>Q[tail]) tail--;
q[++tail]=i,Q[tail]=g[r[x][i]][0]-i;
}
mx[0]=g[r[x][0]][0];
for(int i=1;i<len/2;i++) mx[i]=max(mx[i-1],g[r[x][i]][0]+i);
for(int i=len/2;i<len-1;i++)
tmp=max(tmp,len-i+mx[len/2-len+i]+g[r[x][i]][0]);
ans=max(ans,tmp);
}
void solve(int x,int fa) {
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa&&!mark[i]) {
int v=e[i].to;solve(v,x);
g[v][0]+=e[i].w;
if(g[v][0]>=g[x][0]) g[x][1]=g[x][0],g[x][0]=g[v][0];
else if(g[v][0]>g[x][1]) g[x][1]=g[v][0];
g[v][0]-=e[i].w; // ATTENTION !!!
}
if(x<=n) ans=max(ans,g[x][0]+g[x][1]);
else work(x);
}
int main() {
read(n),read(m);cnt=n;
for(int i=1;i<=m;i++) {
int k;read(k);int v,u;read(v);
for(int j=1;j<k;j++) read(u),ins(u,v,1),v=u;
}
dfs(1,0);solve(1,0);write(ans);
return 0;
}