机房测试11:信息拦截 (tarjan缩点+正反拓扑)

题目:

分析:

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

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

原文地址:https://www.cnblogs.com/mowanying/p/11650896.html

时间: 2024-10-09 05:30:31

机房测试11:信息拦截 (tarjan缩点+正反拓扑)的相关文章

【BZOJ-1924】所驼门王的宝藏 Tarjan缩点(+拓扑排序) + 拓扑图DP

1924: [Sdoi2010]所驼门王的宝藏 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 787  Solved: 318[Submit][Status][Discuss] Description Input 第一行给出三个正整数 N, R, C. 以下 N 行,每行给出一扇传送门的信息,包含三个正整数xi, yi, Ti,表示该传送门设在位于第 xi行第yi列的藏宝宫室,类型为 Ti.Ti是一个1~3间的整数, 1表示可以传送到第 xi行任意

机房测试11:最小生成树(最小生成树+二分)

题目: 分析: 如果直接做最小生成树,会出现以下不合法情况: 1.白边选多了.说明白边的权值太小了,我们可以通过加大白边的权值来似的选少一点白边. 2.白边选少了.与上面同理. 我们不知道白边的取值在多少合适,但明显具有单调性(白边权值越大,选的条数一定会减少),所以可以用二分来确定白边的取值. 二分一个取值,将所有白色边权减去mid,做一遍最小生成树,如果能选到>=need条边,就把mid增大. 细节: 1. =need也要把mid增大. 2. 不能只在白边恰好选到need的时候更新答案,这时

tarjan缩点以及链式前向星的基本+应用(洛谷1262 间谍网络)

题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B.有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报.所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子.因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报. 我们的反间谍机关提供了一份资料,色括所有已知的受贿的间谍,以及他们愿意收受的具体数额.同时我们还知道哪些间谍手中具体掌握了哪些

强连通分量tarjan缩点——POJ2186 Popular Cows

这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定意味着v可达u.    相互可达则属于同一个强连通分量    (Strongly Connected Component, SCC) §有向图和它的转置的强连通分量相同 §所有SCC构成一个DAG(有向无环图) dfn[u]为节点u搜索的次序编号(时间戳),即首次访问u的时间 low[u]为u或u的

P2341 [HAOI2006]受欢迎的牛(tarjan+缩点)

P2341 [HAOI2006]受欢迎的牛 题目描述 每头奶牛都梦想成为牛棚里的明星.被所有奶牛喜欢的奶牛就是一头明星奶牛.所有奶 牛都是自恋狂,每头奶牛总是喜欢自己的.奶牛之间的“喜欢”是可以传递的——如果A喜 欢B,B喜欢C,那么A也喜欢C.牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你 算出有多少头奶牛可以当明星. 输入输出格式 输入格式: ? 第一行:两个用空格分开的整数:N和M ? 第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B 输出格式: ? 第一行:

[BZOJ1179] [Apio2009]Atm(tarjan缩点 + spfa)

传送门 题意 N个点M条边的有向图 每个点有点权 从某一个结点出发 问能获得的最大点权和 一个点的点权最多被计算一次 N<=500000 M<=500000 思路 先tarjan缩点,然后就形成一个dag,无环,所以直接spfa求最长路就行. 也可以先缩点,然后拓扑排序 + dp 搞. 代码 1 #include <map> 2 #include <queue> 3 #include <stack> 4 #include <cstdio> 5 #

hihoCoder 1185 连通性&#183;三(Tarjan缩点+暴力DFS)

#1185 : 连通性·三 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 暑假到了!!小Hi和小Ho为了体验生活,来到了住在大草原的约翰家.今天一大早,约翰因为有事要出去,就拜托小Hi和小Ho忙帮放牧. 约翰家一共有N个草场,每个草场有容量为W[i]的牧草,N个草场之间有M条单向的路径. 小Hi和小Ho需要将牛羊群赶到草场上,当他们吃完一个草场牧草后,继续前往其他草场.当没有可以到达的草场或是能够到达的草场都已经被吃光了之后,小hi和小Ho就把牛羊群赶回家. 一开

POJ 2186 Popular Cows -- tarjan 缩点

链接: POJ 2186 Popular Cows 题意: 每一头牛都希望在牛群里面备受瞩目,在一个牛群中有N头牛(1<=N<=10000),你被给予M(1<=M<=50000)个关系对,形式如(A,B),这意味着A牛认为B牛比它更受欢迎,由于这种欢迎度是满足传递性的,那么若是A牛认为B牛更受欢迎,B牛认为C牛更受欢迎,那么A牛也会认为C牛更受欢迎.你的任务是计算出被所有牛受欢迎的牛的个数. 输入: 第一行两个整数 N 和 M 第2 到 M + 1 行,两个分开的数 A,B,意味着

[BZOJ 1179]ATM题解 Tarjan缩点+SPFA

[BZOJ 1179]ATM题解 Tarjan缩点+SPFA Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 Output 输出一个整数,