zoukankan      html  css  js  c++  java
  • 题解 P4244-[SHOI2008]仙人掌图

    (Large atural) P4244 [SHOI2008]仙人掌图 原题链接

    解法

    一开始看见仙人掌,按套路要建出圆方树,但后来发现其实是不用建的。

    回想一下求普通树的直径时候的 ( ext{dp}) 方法,可爱而无害。

    (o) 为当前 ( ext{dfs}) 到的点, (v) 为它的一个儿子结点,(f_x) 为点 (x) 到叶子结点的最长距离(这个定义全文适用),则有:

    [f_o=max(f_o,f_v+1) ]

    [ans=max(ans,f_o+f_v+1) ]

    现在尝试把它扩展至毒瘤的仙人掌来。

    我们发现,如果 (o) 与儿子 (v) 不共存与一个环,那么它们还是可以用上面的 ( ext{dp}) 方式的。但是如果共存于一个环,那还是要特殊处理。

    ( ext{tarjan}) 的时候,如果 (low_v>dfn_o) 那么就表示 (v)(o) 不在一个环上,就大胆放心地用上面的 ( ext{dp}) 方式。

    如果在一个环中,那么我们就要把这个环抽出来:记录一下每个点是从哪个点搜过来的,记为 (fa)( ext{tarjan}) 的末尾再遍历边一遍,如果找到一个点 (x)(fa_x e o)(dfn_v>dfn_o) 那就说明我们找到了一个环的两个点。然后不断让 (x) 往上跳,即不断令 (x=fa_x) 就可以找到整个环。

    其原理是:从 (o) 开始 ( ext{tarjan}) ,如果有环,会沿着环走,最后到达 (o) 的儿子。而且每次 (x=fa_x) 就相当于沿着搜索轨迹倒退,最后终能复原整个环。

    然后找到了这个环怎么办呢?画画仙人掌,发现底下的东西都已不再重要,只有 (f_o) 值得更新了。所以现在的任务是:在这个环中更新 (ans) 。并更新 (f_o)

    更新 (ans),首先按照环形套路,断环为链,将环复制一倍。找到两个距离不超过半环的点。(如果超过半环了就会反着来,比如环 1-2-3-4 首尾相接,断环成链 1-2-3-4-1-2-3-4 ,1-4 不会统计,因为它路径长度为3,而 4-1会统计)这样处理,我们就可以有一个显然的更新。

    设原来环的长度为 (len) ,存环的数组为 (a) ,现在枚举到了下标 (i)(j) ,使得 (i>j)(i-j<frac{len}2) ,则两个点是(a_i,a_j)

    [ans=max(ans,i-j+f_{ai}+f_{aj}) ]

    这个可以用单调队列优化,设队尾的点是 (x) ,在环中下标为 (y) ,如果 (f_x-y<f_{ai}-i) 就队尾左移。

    更新 (f_o) 简单。设 (x) 为环上一个点(不为 (o)),(l)(x)(o) 的距离。则

    [f_o=max(f_o,f_x+l) ]

    注意更新的顺序。

    然后就做完啦~

    代码

    开了 O2 后,居然是目前的最优解诶

    码风毒瘤见谅

    #include<bits/stdc++.h>
    #define rep(i,x,y) for(int i=x;i<=y;++i)
    #define mar(o) for(int E=fst[o];E;E=e[E].nxt)
    #define v e[E].to
    using namespace std;
    const int n7=50123,l7=100123,m7=500123;
    struct dino{int to,nxt;}e[m7];
    int n,m,ecnt,t,fst[n7],f[n7],ans;
    int dfn[n7],low[n7],fa[n7],lin[l7];
    
    struct MoquE{
    	int head,tail,que[l7];
    	void clear(){head=1,tail=0;que[1]=0;}
    	void updat(int id,int cnt){
    		while(head<=tail && id-que[head]>cnt/2)head++;
    		ans=max(ans,f[ lin[id] ]+f[ lin[ que[head] ] ]+id-que[head]);
    		while(head<=tail && f[ lin[ que[tail] ] ]-que[tail]<f[ lin[id] ]-id)tail--;
    		tail++,que[tail]=id;
    	}
    }qued;
    
    int rd(){
    	int shu=0;char ch=getchar();
    	while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch))shu=(shu<<1)+(shu<<3)+(ch^48),ch=getchar();
    	return shu;
    }
    
    void edge(int sta,int edn){
    	ecnt++;
    	e[ecnt]=(dino){edn,fst[sta]};
    	fst[sta]=ecnt;
    }
    
    void calc(int o1,int o2){
    	int cnt=0;
    	for(int o=o2;o!=o1;o=fa[o]){
    		cnt++,lin[cnt]=o;
    	}
    	cnt++,lin[cnt]=o1;
    	rep(i,1,cnt)lin[i+cnt]=lin[i];
    	qued.clear();
    	rep(i,1,cnt*2)qued.updat(i,cnt);
    	rep(i,1,cnt-1)f[o1]=max(f[o1],f[ lin[i] ]+min(i,cnt-i));
    } 
    
    void tarjan(int o){ 
    	t++,dfn[o]=low[o]=t;
    	mar(o){
    		if(v==fa[o])continue;
    		if(!dfn[v]){
    			fa[v]=o,tarjan(v);
    			low[o]=min(low[o],low[v]);
    		}
    		low[o]=min(low[o],dfn[v]);
    		if(low[v]>dfn[o]){
    			ans=max(ans,f[o]+f[v]+1);
    			f[o]=max(f[o],f[v]+1);
    		}
    	}
    	mar(o){
    		if(fa[v]!=o&&dfn[v]>dfn[o])calc(o,v);
    	}
    }
    
    int main(){
    	n=rd(),m=rd();
    	rep(i,1,m){
    		int num=rd()-1,o1,o2=rd();
    		while(num--)o1=rd(),edge(o1,o2),edge(o2,o1),o2=o1;
    	}
    	tarjan(1);
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    LeetCode 109 Convert Sorted List to Binary Search Tree
    LeetCode 108 Convert Sorted Array to Binary Search Tree
    LeetCode 107. Binary Tree Level Order Traversal II
    LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode 103 Binary Tree Zigzag Level Order Traversal
    LeetCode 102. Binary Tree Level Order Traversal
    LeetCode 104. Maximum Depth of Binary Tree
    接口和多态性
    C# 编码规范
  • 原文地址:https://www.cnblogs.com/BlankAo/p/14244657.html
Copyright © 2011-2022 走看看