原题链接:G. Remove Directed Edges
题目大意:
给出一个 n n n 个点, m m m 条边的有向无环图。你要进行删除一些边的操作,操作后,要保证每一个点的入度和出度都至少减少了 1 1 1(如果原来点的出入度为 0 0 0 则不用进行删边操作)。
在满足这个条件的前提下,求出一个最大的点集
S
S
S 满足任意两点都联通(即
u
→
v
u \rightarrow v
u→v 或
v
→
u
v \rightarrow u
v→u ,某一点能被某一点去到即可,别和我一样读假了,以为是强连通,双向奔赴 )。
问操作后这个点集最大的大小是多少?
解题思路:
一眼 D A G DAG DAG ,联想到拓扑排序。
题目中存在删边操作,而且要保证每一个点的入度出度都至少减少了 1 1 1 (题目要求不考虑 0 0 0 入度和 0 0 0 出度的点)。
求出一个最大的点集 S S S 满足任意两点都联通,即就是两点间有一条路径可以相连。那么这一题就转化为了求一条满足题意的路径,也就是最长路问题。
先考虑最长路怎么求,由于这一题是 D A G DAG DAG 图,我们可以转化成拓扑图,然后从拓扑序小的转移到大的,一定会得到一条最长路满足题意,在这些路径中找出一条最长的即可。
考虑选点,哪一些点能够被选入集合里呢?
对于某一些只存在一条出度,或者一条入度的点来说,这一条边是一定要被删去的,那么这一个点就不能 走出去 / / /被到达 。如果我们提前把这些点放入我们的路径当中,这一些点就会把这一条路径截断,显然不优。
那么我们考虑在选点的时候,我们当前这个点 u u u 的出度 o u t out out 要至少大于 1 1 1,去到的点 v v v 的入度 i n in in 要大于 1 1 1,这样,我们才能保证 u → v u \rightarrow v u→v 这一条路径,在经过题中操作之后不会被删除。
给一个图解释一下:
对于这一张图,有些点入度只有
1
1
1,比如
(
2
,
5
)
(2,5)
(2,5),有些点出度只有
1
1
1,比如
(
3
,
4
,
5
)
(3,4,5)
(3,4,5),把这一些点考虑进去,是不优的,或者说考虑进来,就会把路径截断。因为删除了这些必须删除的边之后,我们的图就会变成这样:
这就是为什么那么我们考虑在选点的时候,保证 u → v u \rightarrow v u→v,我们当前这个点 u u u 的出度 o u t out out 要至少大于 1 1 1,去到的点 v v v 的入度 i n in in 要大于 1 1 1 的原因了。
所以在满足条件的情况下,这张图我们只能构造三个 S S S,分别是 ( 1 , 3 ) (1,3) (1,3), ( 2 , 3 ) (2,3) (2,3), ( 2 , 4 ) (2,4) (2,4),其中集合大小最大为 2 2 2,所以答案为 2 2 2 。
所以我们在拓扑序 d p dp dp 转移时候,当且仅当 u → v u \rightarrow v u→v 满足 o u t u > 1 out_u > 1 outu>1 且 i n v > 1 in_v > 1 inv>1 才能转移。最后统计答案时,在 d p dp dp 数组取一个 m a x max max 即可。
AC代码:
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 2e5 + 10;
vector<int> g[N];//vector存图
void solve()
{
int n, m;
cin >> n >> m;
vector<i64> dp(n + 1);
//由于我们要用到in 所以我们多开一个topin用来跑拓扑序
vector<int> out(n + 1), in(n + 1), topin(n + 1);
for (int i = 0, x, y; i < m; ++i)
{
cin >> x >> y;
g[x].emplace_back(y);
++out[x], ++in[y], ++topin[y];
}
//跑拓扑时顺带dp
queue<int> que;
for (int i = 1; i <= n; ++i)
{
dp[i] = 1;
if (!in[i]) que.push(i);
}
while (que.size())
{
auto t = que.front(); que.pop();
for (auto& x : g[t])
{
if (!--topin[x]) que.push(x);
if (out[t] > 1 && in[x] > 1)//out > 1 且 in > 1 才转移
dp[x] = max(dp[x], dp[t] + 1);
}
}
i64 ans = 1;
for (int i = 1; i <= n; ++i)//统计答案
{
ans = max(ans, dp[i]);
g[i].clear();//顺带多测清空
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1; //cin >> t;
while (t--) solve();
return 0;
}