[bzoj1023] [SHOI2008]cactus仙人掌图

Description

  如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌
图(cactus)。所谓简单回路就是指在图上不重复经过任何一个顶点的回路。

img

  举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(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('\n');}

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;
}
posted @ 2019-01-15 21:54  Hyscere  阅读(177)  评论(0编辑  收藏  举报