zoukankan      html  css  js  c++  java
  • [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('
    ');}
    
    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;
    }
    
  • 相关阅读:
    Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2) D
    HDU 2036 求任意多边形面积板子题
    HDU 6703 array(主席树)
    2019牛客暑期多校训练营(第九场)H Cutting BamboosO(二分、主席树)
    lintcode-425-电话号码的字母组合
    lintcode-81-数据流中位数
    lintcode-424-逆波兰表达式求值
    lintcode-423-有效的括号序列
    lintcode-422-最后一个单词的长度
    lintcode-421-简化路径
  • 原文地址:https://www.cnblogs.com/hbyer/p/10274510.html
Copyright © 2011-2022 走看看