cf的一道题,非常有意思,题目是问图中是否存在两个点,使得这两个点之间有三条路径,而且三条路径没有公共点。
其实就是判断一下是否为仙人掌就行了,如果不是仙人掌的话肯定就存在,题目难在输出路径上,改了半天也不对,借鉴了一个dalao的代码。感觉非常神奇。
首先是判断是否为仙人掌,利用返祖边即可,如果一条树边被两条或者以上的返祖边覆盖,那么图就肯定不是一个仙人掌,利用差分可以实现。
然后是输出路径,找到一个被覆盖了两次的边,那么两条返祖边一定跨过了这个边,往上往下搜索就行了,找到了两条返祖边。那么路径就出来了,一条毫无疑问是树边,一直网上蹦就行了。
另外两个就是返祖边,那么问题来了,起点在哪?因为路径不能有交点(我们设深度小的为起点,深度大的为终点),那么终点一定是两条返祖边的下面端点的lca(这样很明显两个通过返祖边走上去的路径没有交点,而且这两条路径不会沿着树边往上走,他们可以往下走然后沿着非树边跳上去到起点,这样就不会和树边那条路径有交点了),起点就是两条返祖边上面端点中比较深的一个点,因为如果是比较浅的那个点的话,会和树边上的路径有交点。
那么我们就做完了,附上代码。——by VANE
#include<bits/stdc++.h> using namespace std; const int N=200005; int dep[N],fa[N],f[N]; int n,m,l1,l2,r1,r2,s,t,x,y; vector<int> e[N]; void dfs(int x,int father) { dep[x]=dep[father]+1; fa[x]=father; for(int i=0;i<e[x].size();++i) if(!dep[e[x][i]]) dfs(e[x][i],x); else if(dep[e[x][i]]<dep[x]&&e[x][i]!=father) --f[e[x][i]],++f[x]; f[father]+=f[x]; } void DFS(int x) { for(int i=0;i<e[x].size();++i) if(dep[e[x][i]]==dep[x]+1) { DFS(e[x][i]); if(l2) return; } else if(dep[e[x][i]]<dep[s]&&e[x][i]!=fa[x]) { if(l1) r2=x,l2=e[x][i]; else r1=x,l1=e[x][i]; if(l2) return; } } int d[N]; void go(int l,int r) { int k=t;t+=abs(dep[r]-dep[l])+1; if(dep[l]>dep[r]) for(int i=l,j=k+1;j<=t;i=fa[i],++j) d[j]=i; else for(int i=r,j=t;j>k;i=fa[i],--j) d[j]=i; } void print() { printf("%d ",t); for(int i=1;i<=t;++i) printf("%d ",d[i]);puts(""); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { int a,b; scanf("%d%d",&a,&b); e[a].push_back(b); e[b].push_back(a); } for(int i=1;i<=n;++i) if(!dep[i]) dfs(i,0); for(int i=1;i<=n;++i) if(f[i]>1) {s=i;break;} if(!s) {puts("NO");return 0;} DFS(s); puts("YES"); if(dep[l1]>dep[l2]) swap(l1,l2),swap(r1,r2); for(x=r1,y=r2;x!=y;) dep[x]>dep[y]?x=fa[x]:y=fa[y]; t=0;go(x,r1);go(l1,l2);print(); t=0;go(x,r2);go(l2,l2);print(); t=0;go(x,l2);print(); return 0; }