题意:n个点,m条边的无向图,有的边上有标记,每条边只能走一次
给你一个起点,一个终点,询问是否能找到从起点到终点的路径,这条路径至少包含一条含有标记的边
分析:然后边双缩点
下面介绍一下边双的性质
1,删掉边双内任意一条边,不影响边双的连通性
2,任取边双内两个点u,v,对于边双里面的任意一条边,至少包含于一条u到v的路径
所以对于这个题,可以运用上述的第二个性质,对于在边双里的标记边,都是可以经过的
然后缩点以后,变成一棵树,然后从起点所在的边双开始遍历,找到到终点所在边双的路径,询问权值是否大于0就行了
注意一点:这里的权值是点权加边权
#include <iostream> #include <cstdio> #include <cstring> #include <stack> using namespace std; const int N=3e5+5; int n,m,s,t; struct Edge { int u,v,w,next; } edge[N<<1],e[N<<1]; int head[N],tot,h[N]; void add(int u,int v,int w) { edge[tot].u=u; edge[tot].v=v; edge[tot].w=w; edge[tot].next=head[u]; head[u]=tot++; } void adde(int u,int v,int w) { e[tot].v=v; e[tot].w=w; e[tot].next=h[u]; h[u]=tot++; } int pre[N],low[N],clk,bcc,bel[N]; stack<int>S; void targin(int u,int f) { pre[u]=low[u]=++clk; S.push(u); for(int i=head[u]; ~i; i=edge[i].next) { int v=edge[i].v; if(v==f)continue; if(!pre[v]) { targin(v,u); low[u]=min(low[v],low[u]); } else low[u]=min(pre[v],low[u]); } if(pre[u]==low[u]) { ++bcc; int k; do { k=S.top(); S.pop(); bel[k]=bcc; } while(k!=u); } } int val[N]; bool get(int u,int f,int sum) { sum+=val[u]; if(u==t)return sum; for(int i=h[u]; ~i; i=e[i].next) { int v=e[i].v; if(v==f)continue; if(get(v,u,sum+e[i].w))return true; } return false; } int main() { scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); int tmp=0; for(int i=1; i<=m; ++i) { int u,v,w; scanf("%d%d%d",&u,&v,&w),tmp+=w; add(u,v,w),add(v,u,w); } scanf("%d%d",&s,&t); if(tmp==0) { printf("NO "); return 0; } targin(s,-1); tmp=tot; memset(h,-1,sizeof(h)),tot=0; for(int i=0; i<tmp; i=i+2) { int u=edge[i].u,v=edge[i].v; u=bel[u],v=bel[v]; if(u==v)val[u]+=edge[i].w; else adde(u,v,edge[i].w),adde(v,u,edge[i].w); } s=bel[s]; t=bel[t]; if(get(s,-1,0))printf("YES "); else printf("NO "); return 0; }