题目:

 

 

 

 分析:

这道题真的毒瘤,思想很简单,但是细节很多。。

题意:找到从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
*/
View Code

 

posted on 2019-10-11 19:41  rua-rua-rua  阅读(155)  评论(0编辑  收藏  举报