标签:print min flag out 答案 oid class false isp
这道题真的毒瘤,思想很简单,但是细节很多。。
题意:找到从1~n的必经点(每条信息都能获取),且不在一个点数>=2 的强连通分量中(恰好获取一次)。
先将有向图缩点,转换成一张有向无环图。
然后对缩点后的图进行正反拓扑,求出必经点。
再看必经点是否在一个点数>=2的强联通分量中。
正反拓扑过程:
fs[u]表示从起点到u的路径条数,ft[u]表示从终点到u的路径条数
由乘法原理可知,若一个点满足:fs[u]*ft[u]==fs[t]
这个点是必经点。
细节:
1. 对于存在自环的点,一定不能为最后答案,所以要用self标记一下。
2. 要求用按照遍历顺序输出,要用拓扑序记录一下遍历顺序。
3. 缩点从1出发,而不是将整张图的强联通分量都找出来!!
4. 拓扑的起点为1,终点为n。
5. 缩点后的连边可能会有重边,但这并不影响答案。
6. 路径数会很大,对大质数取模即可(可能会冲突)。
#include<bits/stdc++.h> using namespace std; #define ri register int #define N 1000005 #define ll long long const ll mod=1e9+7; int read() { int x=0,fl=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) fl=-1; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); return x*fl; } int n,m,tot=0,cnt=0,Ti=0,head[N],nex[N],to[N],fa[N],self[N]; int dfn[N],low[N],stk[N],top=0,bel[N],ru1[N],ru2[N],flag[N],siz[N]; ll fs[N],ft[N]; vector<int> e1[N]; vector<int> e2[N]; vector<int> ans; vector<int> scc[N]; void init() { tot=Ti=cnt=top=0; ans.clear(); for(ri i=1;i<=max(tot,n);++i) self[i]=siz[i]=dfn[i]=low[i]=stk[i]=bel[i]=ru1[i]=ru2[i]=head[i]=fs[i]=ft[i]=flag[i]=0,e1[i].clear(),e2[i].clear(),scc[i].clear(); } void add(int a,int b) { if(a==b) { self[a]=true; return ; } to[++tot]=b; nex[tot]=head[a]; head[a]=tot; } void tarjan(int x) { dfn[x]=low[x]=++Ti; if(x==n) siz[x]=1; stk[++top]=x; flag[x]=true; for(ri i=head[x];i;i=nex[i]){ int v=to[i]; if(!dfn[v]){ tarjan(v); low[x]=min(low[x],low[v]); } else if(flag[v]) low[x]=min(low[x],dfn[v]); siz[x]|=siz[v]; } if(dfn[x] == low[x]){ cnt++; do{ int xx=stk[top]; scc[cnt].push_back(xx); flag[xx]=false; bel[xx]=cnt; }while(stk[top--]!=x); } } queue<int> q; int tup[N]; void topu() { int num=0; q.push(bel[1]); fs[bel[1]]=1; while(!q.empty()){ int u=q.front(); q.pop(); tup[++num]=u; for(ri i=0;i<e1[u].size();++i){ int v=e1[u][i]; fs[v]=fs[v]+fs[u]; if(fs[v]>=mod) fs[v]%=mod; if(--ru1[v]==0) q.push(v); } } q.push(bel[n]); ft[bel[n]]=1; while(!q.empty()){ int u=q.front(); q.pop(); for(ri i=0;i<e2[u].size();++i){ int v=e2[u][i]; ft[v]=ft[v]+ft[u]; if(ft[v]>=mod) ft[v]%=mod; if(--ru2[v]==0) q.push(v); } } for(ri i=1;i<=num;++i){ int u=tup[i]; if( fs[u]*ft[u]%mod==fs[bel[n]] && scc[u].size()==1 && !self[scc[u][0]]) ans.push_back(scc[u][0]); } printf("%d\n",ans.size()); for(ri i=0;i<ans.size();++i) printf("%d ",ans[i]); printf("\n"); } int main() { freopen("i.in","r",stdin); freopen("i.out","w",stdout); int T=read(); while(T--){ init(); n=read(), m=read(); int a,b; for(ri i=1;i<=m;++i) a=read(),b=read(),add(a,b); tarjan(1); for(ri i=1;i<=n;++i) if(dfn[i] && siz[i]) for(ri j=head[i];j;j=nex[j]){ int v=to[j]; if(bel[i]==bel[v] || !siz[v]) continue;//chong bian e1[bel[i]].push_back(bel[v]); ru1[bel[v]]++; e2[bel[v]].push_back(bel[i]); ru2[bel[i]]++; } topu(); } } /* 6 4 3 2 4 1 3 3 2 2 2 1 2 2 1 3 1 2 3 4 4 1 2 2 4 3 4 1 3 6 6 1 3 3 2 2 1 1 4 5 4 4 6 7 9 1 2 2 4 2 3 4 3 3 6 6 3 3 5 6 5 5 7 */
标签:print min flag out 答案 oid class false isp
原文地址:https://www.cnblogs.com/mowanying/p/11650896.html