强连通缩点/最长路 (石油大学组队赛 K: Birdwatching)
Posted Kalzn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了强连通缩点/最长路 (石油大学组队赛 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;
以上是关于强连通缩点/最长路 (石油大学组队赛 K: Birdwatching)的主要内容,如果未能解决你的问题,请参考以下文章