强连通缩点/最长路 (石油大学组队赛 K: Birdwatching)

在这里插入图片描述
题意:给你一个有向图 G G G,和其中的一个点 T T T,你需要找到所有的满足以下条件的点 T ′ T' T
1、 < T ′ , T > ∈ G <T',T>\in G <T,T>G
2、简单路径 < T ′ , T > <T',T> <T,T>只有一条
题解:这个题起初大家交的很多,因为这个题看起来非常的简单,我也是,然后就直接建反图跑最短路上了。然后就wa了,一看大家都是73point。后来仔细想想,好像这个题没有这么的简单。我一开始的思路是:直接建反图,然后跑可达点。然后枚举每一个T的入点。看看原题是不是有一条边令该点连接一个可达点,如果有,本点不符合要求,否则,符合要求。交上去wa。后来仔细想,发现自己非常白痴,因为这个可达点可以是由这个点可达的,如果此时图中有环,上述结论就不对。
所以正确姿势(我自己的姿势):先把T从图中刨出来,然后跑tarjan,把强连通跑出来缩点,缩成DAG,然后在DAG上以T的所有入点为起点跑最长路,然后枚举所有T的入点,满足以下要求的,是符合要求的点:
1、在 T ′ T' T所属的强联通块内,只有它一个T的入度点。
2、 T ′ T' T所属的强联通块的最长路为0。

下面是ac代码(比赛的时候文件头吸氧了,写了个spfa莫名其妙的过了,赛后一看2900+ms,遂用dij交了一发180ms):

// % everyone
#include <cstdio>
#include<iostream>
#include<cstring>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <cctype>
#include <time.h>

namespace IO {
    double start_time = 0.0;
    void ct() { start_time = clock(); return; }
    void fast_cin() { std::ios::sync_with_stdio(false); std::cin.tie(); }
    void read_f(int flag = 0) { freopen("0.in", "r", stdin); if(!flag) freopen("0.out", "w", stdout); }
    void run_time() { std::cout << "\nESC in : " << ( clock() - start_time ) * 1000.0 / CLOCKS_PER_SEC << "ms" << std::endl; }
}
using namespace IO;
template <typename T>
bool bacmp(const T & a, const T & b) { return a > b; }
template <typename T>
bool pecmp(const T & a, const T & b) { return a < b; }
 
#define ll long long
#define ull unsigned ll
#define _min(x, y) ((x)>(y)?(y):(x))
#define _max(x, y) ((x)>(y)?(x):(y))
#define max3(x, y, z) ( max( (x), max( (y), (z) ) ) )
#define min3(x, y, z) ( min( (x), min( (y), (z) ) ) )
#define pr make_pair
#define pb push_back
using namespace std;

const int N = 3e5+5;
int n, m, cnt;
int he[N], ver[N], ne[N], tot;
int dfn[N], low[N], Belong[N], bcnt;
int st[N], ins[N], top;
map<pair<int, int>, int> mp;
int t;
void add(int x, int y)
{
    ver[++tot] = y;
    ne[tot] = he[x];
    he[x] = tot;
}
void tarjan(int x)
{
    if (x == t) return;
    dfn[x] = low[x] = ++cnt;
    ins[x] = 1;
    st[++top] = x;
    for (int i = he[x]; i; i = ne[i])
    {
        int y =ver[i];
        if (y == t) continue;
        if (!dfn[y])
        {
            tarjan(y);
            low[x] = min(low[x], low[y]);
        }
        else if (ins[y]) low[x] = min(low[x], dfn[y]);
    }
    if (dfn[x] == low[x])
    {
        bcnt++;
        int y;
        do
        {
            y = st[top--];
            ins[y] = 0;
            Belong[y] = bcnt;
        } while(y != x);
    }
}
int d[N], c[N];
int the[N], tver[N], tne[N], ttot;
void init(int n)
{
    top = cnt = bcnt = 0;
    tot = 1, ttot = 1;
    for (int i = 0; i <= n; i++)
    {
        he[i] = 0, ins[i] = 0, d[i] = 0, c[i] = 0, dfn[i] = 0, low[i] = 0;
        the[i] = 0;
    }
}
void tadd(int x, int y)
{
    tver[++ttot] = y;
    tne[ttot] = the[x];
    the[x] = ttot;
}
int vis[N];
vector<int> G[N];
int is[N];
int sum[N];
void dfs(int x)
{
    if (vis[x]) return;
    vis[x] = 1;
    for (int i = the[x]; i; i = tne[i])
    {
        int y = tver[i];
        dfs(y);
    }
    for (int i = 0; i < G[x].size(); i++)
    {
        is[G[x][i]] = 1;
    }
}
int dis[N], v[N];
void spfa()
{
    priority_queue<pair<int, int> > q;
    for (int j = he[t]; j; j = ne[j])
    {
        int y = ver[j];
        q.push( pr(0, Belong[y]) );
    }
    while(q.size())
    {
        int te = q.top().second; q.pop();
		if (v[te]) continue;
		v[te] = 1;
        //cout <<"s"<< te << endl;
        for (int i = the[te]; i; i = tne[i])
        {
            int y = tver[i];
            if (dis[y] > dis[te] + 1) continue;
            dis[y] = dis[te] + 1;
            q.push(pr(dis[y], y));
        }
    }
}
int main()
{
    int n, m; scanf("%d%d%d", &n, &m, &t);
    t++;
    init(n);
    while(m--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        x++;y++;
        add(y, x);

    }
    for (int i = 1; i <= n; i++) if (!dfn[i])
        tarjan(i);
    for (int i = 1; i <= n; i++)
    {
        if (i == t) continue;
        for (int j = he[i]; j; j = ne[j])
        {
            int  y =ver[j];
            if (y == t) continue;
            if (Belong[i] != Belong[y] && mp[pr(Belong[i], Belong[y])]== 0)
            {
                mp[pr(Belong[i], Belong[y])] = 1;
                tadd(Belong[i], Belong[y]);
            }
        }
    }
    spfa();
    for (int j = he[t]; j; j = ne[j])
    {
        int y = ver[j];
        sum[Belong[y]]++;
    }
    set<int> ans;
    for (int j = he[t]; j; j = ne[j])
    {
        int y = ver[j];
        if (dis[Belong[y]] == 0 && sum[Belong[y]] == 1) ans.insert(y);
    }
    printf("%d\n", ans.size());
    for (auto x : ans)
    {
        printf("%d\n", x-1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值