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;
}
posted @ 2021-11-08 18:32  !^^!  阅读(23)  评论(0编辑  收藏  举报