zoukankan      html  css  js  c++  java
  • 「CF521E」 Cycling City

    「CF521E」 Cycling City

    传送门

    首先你能发现这个东西一定是两个环的公共边。

    最开始想的是什么如果一个点被访问过三次那它一定是公共边的某一端之类的东西,然后发现被仙人掌叉掉。

    然后就不会了。

    事实上有很简洁的做法:先求出原图的任意一棵 ( exttt{DFS}) 树,然后对于每一条非树边,它一定与一条树上的路径构成一个环,暴力覆盖知道某一条边被经过两次即可。

    根据抽屉原理可得这样的复杂度是正确的,为 (O(n))

    当然我为了方便写的 (O(nlog_2n))

    以后遇到与环相关的问题可以往这个方向上想想。

    贴代码:

    /*---Author:HenryHuang---*/
    /*---Never Settle---*/
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+5;
    struct edge{
    	int to,nex;
    }e[maxn<<1];
    int head[maxn],cnt=1;
    void add(int a,int b){
    	e[++cnt]=(edge){b,head[a]};
    	head[a]=cnt;
    }
    int vis[maxn],t[maxn<<2];
    int dep[maxn],fa[maxn];
    void dfs(int u){
    	dep[u]=dep[fa[u]]+1;vis[u]=1;
    	for(int i=head[u];i;i=e[i].nex){
    		int v=e[i].to;
    		if(!vis[v]){
    			t[i]=t[i^1]=1;
    			fa[v]=u;
    			dfs(v);
    		}
    	}
    }
    int path[maxn],tot;
    void wr(int a,int b){
    	path[++tot]=a;
    	while(a!=b){
    		a=fa[a];
    		path[++tot]=a;
    	}
    	return ;
    }
    void pr(){
    	cout<<tot<<' ';
    	for(int i=1;i<=tot;++i) cout<<path[i]<<' ';
    	cout<<'
    ';tot=0; 
    }
    int lca(int x,int y){
    	while(dep[x]>dep[y]) x=fa[x];
    	while(dep[x]<dep[y]) y=fa[y];
    	while(x!=y) x=fa[x],y=fa[y];
    	return x;
    }
    void print(int a,int b,int c,int d){
    	cout<<"YES
    ";
    	int x=lca(b,d);
    	if(dep[a]>dep[c]) swap(a,c),swap(b,d);
    	wr(x,c);reverse(path+1,path+tot+1);pr();
    	wr(c,a);wr(b,x);pr();
    	path[++tot]=c,wr(d,x);pr();
    	exit(0);
    }
    map<pair<int,int>,pair<int,int> > mp;
    void check(int a,int b){
    	if(dep[a]<dep[b]) swap(a,b);
    	int d=a;
    	while(d!=b){
    		int c=fa[d];
    		if(mp.count(make_pair(c,d)))
    			print(b,a,mp[make_pair(c,d)].first,mp[make_pair(c,d)].second);
    		else mp[make_pair(c,d)]=make_pair(b,a);
    		d=c;
    	}
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	int n,m;cin>>n>>m;
    	for(int i=1;i<=m;++i){
    		int a,b;cin>>a>>b;
    		add(a,b),add(b,a);
    	}
    	for(int i=1;i<=n;++i)
    		if(!vis[i]) dfs(i);
    	for(int u=1;u<=n;++u)
    		for(int i=head[u];i;i=e[i].nex)
    			if(!t[i])
    				check(u,e[i].to),t[i]=t[i^1]=1;
    	cout<<"NO
    ";
    	return 0;
    }
    
    在繁华中沉淀自我,在乱世中静静伫立,一笔一划,雕刻时光。
  • 相关阅读:
    键盘过滤驱动
    多线程和多进程的差别(小结)
    Android UI设计规则
    怎样使用SetTimer MFC 够具体
    Chord算法(原理)
    POJ 1384 Piggy-Bank 背包DP
    Bulk Insert命令具体
    hibernate官方新手教程 (转载)
    教你用笔记本破解无线路由器password
    转换流--OutputStreamWriter类与InputStreamReader类
  • 原文地址:https://www.cnblogs.com/HenryHuang-Never-Settle/p/solution-CF521E.html
Copyright © 2011-2022 走看看