E. Two Houses——CodeCraft-21 and Codeforces Round #711 (Div. 2)
E. Two Houses
https://codeforces.com/contest/1498/problem/E
题目大意
给出一个有\(n\)个点的竞赛图中每个点的入度,你可以询问两个点\(A,B\)之间是否存在从\(A\)到\(B\)的边,如果不存在则输出\("NO"\),否则输出\("YES"\),你需要找到两个点满足互相可达并且入度差最大。并且在输出\("YES"\)时必须立即输出答案不可以再询问,并且不能询问相同的问题。
解题思路
竞赛图有一个结论就是它的强连通分量缩点后是一条链,因此可以得出答案两点必在同一个强连通分量中,因为如果不在同一个强连通分量中,竞赛图缩点后是一条链,则只存在一条边连接两个强连通分量,无法满足互相可达。因此答案必在同一个强连通分量中。
并且按入度排序后强连通分量的分布是连续的。
强连通分量满足入度和等于\((i - 1) * i / 2\),\(i\)是点数,所以只需要按入度排序,然后\(O(n)\)遍历一遍即可,每次从强连通分量中找答案即可。
这里\(i * (i - 1) / 2 = sum(i)\)时,上一个强连通分量的结尾\(last\),则新的强连通分量为 \(last + 1, i\),
下证:
- 设上一个强连通分量的结尾是\(i\),当前点为\(j\),且满足\(sum(i) = i * (i - 1 ) / 2, sum(j) = j * (j - 1) / 2\),设\(k = j - i\)。
- 则\(sum(j) - sum(i) = k * (k - 1) / 2 + i * k\)
上式中\(i * k\)为两个连通分量连接时增加的入度,因为两个强连通分量中的任意两个点都可达(单向),所以增加了\(i * k\)条边,每条边对入度有\(1\)的贡献。
故\(k * (k - 1) / 2 = j * (j - 1) / 2 - i * (i - 1) / 2\).
所以记录上一个强连通分量的最后一个点即可。
Code
#include <bits/stdc++.h>
using namespace std;
pair<int,int> a[507];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
{
cin >> a[i].first;
a[i].second = i;
}
sort(a+1,a+1+n);
int x = 0, y = 0, sum = 0, ans = 0, last = 0;
for(int i = 1; i <= n; i++){
sum += a[i].first;
if(sum == i * (i - 1) / 2){
if(i != last + 1){
if(a[i].first - a[last + 1].first >= ans){
ans = a[i].first - a[last + 1].first;
y = a[i].second;
x = a[last + 1].second;
}
}
last = i;
}
}
cout << "! " << x << " " << y << endl;
}
int main()
{
solve();
return 0;
}
Code will change the world !