zoukankan      html  css  js  c++  java
  • [SHOI2006]仙人掌

    [SHOI2006]仙人掌

    简要解析

    其实很简单
    只要普通树形 (dp) 就行了
    (f_x) 表示 (x) 能向下延深的最大距离,(v)(x) 的儿子
    当一个点不属于任何环时 (f_x = max(f_v + 1))
    这是更新 (ans = max(ans , f_x + f_v + 1))
    只是带环的话,环要单独算
    这是我们的直径可以不经过环顶端的点,直接选环中两个点 (u,v)
    (ans = max(ans , f_u + f_v + dist_{u,v}))
    显然不能 (n^2) 枚举这两个环中点
    因为要符合最短路,所以这两个点距离 (dist_{u,v} leq lim)(lim) 为环长的一半
    那么我们可以再 (dp)(f_u + f_v + dist_{u,v})
    (u,v) 规定方向,从离环顶距离小的往大
    于是破环成链再倍长,单调队列维护

    (Code)

    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    const int N = 100005;
    int n , m , tot , dfc;
    int h[N] , dfn[N] , low[N] , fa[N] , f[N] , a[N] , q[N] , ans;
    
    struct edge{
    	int to , nxt;
    }e[N << 1];
    void add(int x , int y){e[++tot] = edge{y , h[x]} , h[x] = tot;}
    
    void solve(int x , int v)
    {
    	int lim , cnt = 0 , h = 1 , r = 1;
    	for(register int i = v; i != fa[x]; i = fa[i]) a[++cnt] = f[i];
    	for(register int i = 1; i <= cnt; i++) a[i + cnt] = a[i];
    	lim = cnt >> 1 , q[1] = 1;
    	for(register int i = 2; i <= cnt * 2; i++)
    	{
    		while (h < r && i - q[h] > lim) h++;
    		ans = max(ans , i - q[h] + a[i] + a[q[h]]);
    		while (r >= h && a[q[r]] - q[r] <= a[i] - i) r--;
    		q[++r] = i;
    	}
    	for(register int i = 1; i <= cnt; i++) f[x] = max(f[x] , a[i] + min(i , cnt - i));
    }
    
    void tarjan(int x)
    {
    	dfn[x] = low[x] = ++dfc;
    	int v;
    	for(register int i = h[x]; i; i = e[i].nxt)
    	{
    		v = e[i].to;
    		if (v == fa[x]) continue;
    		if (!dfn[v]) fa[v] = x , tarjan(v) , low[x] = min(low[x] , low[v]);
    		else low[x] = min(low[x] , dfn[v]);
    		if (low[v] > dfn[x])
    		{
    			ans = max(ans , f[x] + f[v] + 1);
    			f[x] = max(f[x] , f[v] + 1);
    		} 
    	}
    	for(register int i = h[x]; i; i = e[i].nxt)
    	if (fa[v = e[i].to] != x && dfn[v] > dfn[x]) solve(x , v);
    }
    
    int main()
    {
    	scanf("%d%d" , &n , &m);
    	int num , x , y;
    	for(register int i = 1; i <= m; i++)
    	{
    		scanf("%d%d" , &num , &x);
    		for(register int j = 2; j <= num; j++) 
    			scanf("%d" , &y) , add(x , y) , add(y , x) , x = y;
    	}
    	tarjan(1);
    	printf("%d" , ans);
    } 
    
  • 相关阅读:
    css3新特性总结
    H5新特性总结
    小程序本地移除有一条数据
    字符串截取(某个指定字符前面和后面的值)(指定前几位后几位)
    uni-app 创建项目
    数组转数组对象及数组对象中的某个属性值拼成一个数组
    VUE 解决单页使用keep-alive页面返回不刷新的问题
    小程序弹窗真机不动
    js 数组去重方法
    VUE 列表页中实现分页(下拉到底部触发下一页 )
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/13641463.html
Copyright © 2011-2022 走看看