【NOIP模拟】夕阳 题面 分析 代码
“我有个愿望,我希望在灿烂千阳时遇见你。”
是啊,我也希望。
这是个有n个点的世界,有m条无向边连接着这n个点,但是不保证点之间能够互相到达。
“这个世界的夕阳,只在奇数长的简单路径的尽头。”一个神如是说。
于是我想知道对于一个点对(x,y),x到y之间的所有简单路径中是否存在长度为奇数的路径,只有这样,我才能找到存在有夕阳的路。
对于50%的数据,1≤n,m,q≤500
对于100%的数据,,1≤n,q,m≤100000
分析
比较困难的是到底怎么判这个奇边呢?
多画图,会发现,u和v之间有奇边无非两种情况。
1.dis{u,v}&1==0
2.dis{u,v}&1==1,但是u和v的路径上,有一条边在一个奇环内。意思是,如果一个奇环边数是5,本来u,v之间的距离是4,可以从奇环绕着走,距离变为4-1+5-1=7。
所以,在计算点双联通分量的同时,一旦遇到了返祖边,就判断此环是否是奇环,如果是,退栈的过程中将这个环的所有点都标记上。
最后用lca可求两点距离,用于判断情况1。同时我们维护了每个点在多少个奇环内,也可以通过lca的奇环数量与u,v两点的奇环数量,判断u,v是否有边在奇环内。
代码
#include<bits/stdc++.h> using namespace std; #define N 100100 int n,m,op,ans,cnt,gro,tot,top,tim; int fa[N][20],first[N],col[N],dfn[N],low[N]; int s[N],sta[N],ins[N],odd[N],dep[N]; struct email { int u,v; int nxt; }e[N*4]; queue<int>q; inline void add(int u,int v) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v; } inline void read(int &x) { x=0;int fh=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')fh=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=fh; } void tarjan(int u,int pre) { dfn[u]=low[u]=++tot; sta[++top]=u;ins[u]=1; for(int i=1;i<=tim;i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(v==pre)continue; if(!dfn[v]) { fa[v][0]=u; dep[v]=dep[u]+1; tarjan(v,u); low[u]=min(low[u],low[v]); } else if(ins[v]) { low[u]=min(low[u],dfn[v]); if(dep[u]%2==dep[v]%2)odd[u]=1; } } ins[u]=0; if(dfn[u]==low[u]) { int pos=top,w=0; while(sta[pos]!=u)w|=odd[sta[pos--]]; if(w) { for(int i=pos+1;i<=top;i++) s[sta[i]]++; } gro++; for(int i=pos;i<=top;i++) col[sta[i]]=gro,ins[sta[i]]=0; top=pos-1; } } inline int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int t=dep[x]-dep[y]; for(int i=0;(1<<i)<=t;i++) if(t&(1<<i)) x=fa[x][i]; if(x==y)return x; for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } void dfs(int u) { for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(fa[v][0]==u) { s[v]+=s[u]; dfs(v); } } } int main() { read(n);read(m); for(int i=1;i<=m;i++) { int u,v; read(u);read(v); add(u,v);add(v,u); } tim=log(n)/log(2)+1; for(int i=1;i<=n;i++) if(!col[i]) { dep[i]=1; fa[i][0]=i; tarjan(i,i); } for(int i=1;i<=n;i++) if(fa[i][0]==i) dfs(i); read(op); for(int i=1;i<=op;i++) { int u,v; read(u);read(v); if(fa[u][tim]!=fa[v][tim]) printf("No "); else { int pref=lca(u,v); if ((dep[u]+dep[v]-dep[pref]*2)%2==1||s[u]+s[v]-s[pref]*2>0) printf("Yes "); else printf("No "); } } return 0; }
送一组考场上造的小样例
input 23 22 1 2 1 3 3 9 8 9 2 4 4 5 5 8 5 6 5 7 10 11 11 12 14 15 15 16 16 17 14 17 18 19 18 20 19 20 20 21 20 22 21 23 22 23 11 10 20 12 8 2 4 7 9 1 5 1 7 13 13 1 1 20 19 14 18 4 9 output No No Yes Yes Yes Yes No No Yes No Yes