• loj #3077. 「2019 集训队互测 Day 4」绝目编诗【虚树】


    loj #3077

    Description

    给出一个简单无向图,判断是否存在两个长度相同的简单环。

    (nle 10^4,mle 10^6)

    Solution

    首先考虑朴素做法,从每个点出发暴力搜索,每次回到出发点则计算答案。这样计算的过程中,一个长为 (n) 的环会被从每个节点出发向左右个遍历到一次,一共遍历了 (2n) 次,因此如果长度为 (n) 的环出现了 (>2n) 次,则说明存在两个长度相同的简单环。

    考虑优化,因为图中至多存在 (n-2) 个环(长度为 (3sim n)),而在图的一棵生成树上,增加任意一条非树边都会使图中至少多出一个环,因此图中的边数不超过 (n-1+n-2=2n-3) 条,大于 (2n-3) 则答案一定为 (YES).

    注意到环长总和最多为 (3+4+dots+nle n^2),因此如果我们能保证每次遍历都在访问当前没考虑过的环,那么总复杂度就是 (mathcal O(n^3)) 的(每个环被访问 (mathcal O(n)) 次)。而保证这一点也很简单,在走到新的一点时,进行一遍 (dfs) 看从该点能否回到起点,如果能回到则说明这是环边,继续遍历,否则不遍历该点。(dfs) 的复杂度为 (mathcal O(n+m)),因此总复杂度为 (mathcal O(n^4))

    这个边的上界依然非常松,能不能卡的更紧一些?

    考虑最极限的有长为 (3sim n) 的环各一个的图,在图中随机删边,每条边都有 (sqrt{n}) 的概率被删除,那么删去的边的数量为 (mathcal O(sqrt{n})) ,一个环依然存在的概率为:

    [(1-dfrac 1{sqrt{n}})^{len} ]

    其中 (len) 为该环的长度。

    因此图中剩余环数量的期望即为:

    [sum_{i=3}^{n}(1-dfrac 1{sqrt{n}})^{i} ]

    (q=1-dfrac 1{sqrt{n}}),则期望为:

    [sum_{i=3}^{n}q^i=dfrac{q^3-q^{n-1}}{1-q}<dfrac{1}{1-q}=sqrt{n} ]

    因此图中剩余环数量期望为 (sqrt{n}) ,在此基础上再删去 (sqrt{n}) 条边,就可以是原图没有环,只剩 (n-1) 条边,至此,我们总共只删除了 (2sqrt{n}) 条边,就是原图只剩 (n-1) 条边,因此 (mle n+2sqrt{n})

    在这样的图上再找到一个 (dfs) 树,那么树上有 (mathcal O(sqrt{n})) 条非树边,以这些非树边的顶点建立虚树,再将原图上的非树边加入虚树中,那么原图中的任何一个环都会在虚树上出现。

    因此,s我们只需要再虚树这张仅有 (mathcal O(sqrt{n})) 个点的图上跑之前的 (mathcal O(n^4)) 的算法,复杂度为 (mathcal O(n^2)) 的算法,可以通过此题,需要注意的是,新图中某个长度为 (len) 的环被遍历的次数不再是 (2len) 次,而是 (2) 倍的虚树中环上点数,需要额外记录当前环在虚树上的点数 (cnt)。如果新环的 (cnt) 与旧环不同,那么直接 (Yes),否则根据遍历次数是否 (>2cnt) 进行统计即可。

    最终复杂度为 (mathcal O(n^2)) ,可以通过此题。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e4+10,M=2e4+10;
    int n,m;
    struct node{
    	int u,v,w,nxt;
    };
    
    namespace vt{
    	node e[M<<1];
    	int rt,pd[N],tot,ct[N],first[N],cnt=1,siz[N];
    	bool vis[N];
    	inline void add(int u,int v,int w){
    		e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=first[u];first[u]=cnt;
    		e[++cnt].v=u;e[cnt].w=w;e[cnt].nxt=first[v];first[v]=cnt;
    	}
    	inline bool trans(int u,int last){
    		pd[u]=tot;
    		for(int i=first[u];i;i=e[i].nxt){
    			int v=e[i].v;
    			if(i==(last^1)) continue;
    			if(v==rt) return 1;
    			if(pd[v]==tot||vis[v]) continue;
    			if(trans(v,i)) return 1;
    		}
    		return 0;
    	}
    	inline void dfs(int u,int fa,int last,int len,int sum){
    		++tot;
    		if(!trans(u,last)) return ;
    		vis[u]=1;
    		for(int i=first[u];i;i=e[i].nxt){
    			int v=e[i].v;
    			if(i==(last^1)) continue;
    			if(vis[v]){
    				if(v!=rt) continue;
    				int now=sum+e[i].w;
    				if(!siz[now]){siz[now]=len;ct[now]=1;continue;}
    				if(siz[now]!=len){puts("Yes");exit(0);}
    				if((++ct[now])>(len<<1)){puts("Yes");exit(0);}	
    			}
    			else dfs(v,u,i,len+1,sum+e[i].w);
    		}	
    		vis[u]=0;
    	}
    	inline void solve(){
    		for(int i=1;i<=n;++i) rt=i,dfs(i,0,0,1,0);
    		puts("No");
    	}
    }
    
    int stk[N],top;
    namespace Rt{
    	node e[M<<1];
    	int first[N],cnt,dfn[N],tot,pa[N][15],dep[N],s[N]; 
    	inline void add(int u,int v){e[++cnt].v=v;e[cnt].u=u;e[cnt].nxt=first[u];first[u]=cnt;}
    	inline void dfs(int u,int fa){
    		dfn[u]=++tot;dep[u]=dep[fa]+1;
    		pa[u][0]=fa;
    		for(int i=1;(1<<i)<=dep[u];++i) pa[u][i]=pa[pa[u][i-1]][i-1];
    		for(int i=first[u];i;i=e[i].nxt){
    			int v=e[i].v;
    			if(v==fa) continue;
    			if(dfn[v]){
    				if(dfn[v]>dfn[u]) stk[++top]=u,stk[++top]=v,vt::add(u,v,1);
    			}
    			else dfs(v,u);
    		}
    	}
    	inline bool cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
    	
    	inline int LCA(int u,int v){
    		if(dep[u]<dep[v]) swap(u,v);
    		int t=dep[u]-dep[v];
    		for(int i=14;i>=0;--i) if(t&(1<<i)) u=pa[u][i];
    		if(u==v) return u;
    		for(int i=14;i>=0;--i) if(pa[u][i]!=pa[v][i]) u=pa[u][i],v=pa[v][i];
    		return pa[u][0];
    	}
    	inline int getdis(int u,int v){return dep[u]+dep[v]-2*dep[LCA(u,v)];}
    	
    	inline void build(){
    		sort(stk+1,stk+top+1,cmp);
    		int m=unique(stk+1,stk+top+1)-stk-1;
    		
    		s[top=1]=stk[1];
    		for(int i=2;i<=m;++i){
    			int u=stk[i],lca=LCA(s[top],u);
    			while(top>1&&dep[lca]<=dep[s[top-1]])
    				vt::add(s[top-1],s[top],getdis(s[top-1],s[top])),top--;
    			if(s[top]!=lca) vt::add(lca,s[top],getdis(s[top],lca)),s[top]=lca;
    			s[++top]=u; 
    		}
    		while(top>1) vt::add(s[top-1],s[top],getdis(s[top-1],s[top])),top--;
    	}
    }
    using namespace Rt;
    
    int main(){
    	scanf("%d%d",&n,&m);
    	if(m>=n+2*sqrt(n)){puts("Yes");return 0;}
    	for(int i=1,u,v;i<=m;++i){
    		scanf("%d%d",&u,&v);
    		add(u,v);add(v,u);
    	}
    	for(int i=1;i<=n;++i){
    		if(!dfn[i]){
    			top=0;
    			dfs(i,0);
    			build();
    		}	
    	}
    	vt::solve();
    	return 0;
    }
    
  • 相关阅读:
    Java CodeForces 1230A
    基于Lucene的全文检索实践
    错误解决:ElasticSearch SearchResponse的Hits[]总是比totalHits少一条记录
    Java CodeForces 266A
    jQuery 幻灯片插件 OwlCarousel22.3.4 使用
    由于找不到 MSVCP100.dll,无法继续执行代码。重新安装程序可能会解决此问题。
    idea 单行注释,紧跟代码第一列
    TrayPwrD3 电脑一直处于使用独显的状态
    ubuntu20.04 域名解析暂时失败
    jQuery 幻灯片插件 owl_carousev1.31 使用
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/14845191.html
Copyright © 2020-2023  润新知