Codeforces 1498E Two Houses 题解 —— 如何用结论吊打标算
\(\mathcal{Translate}\)
有一个 \(n\) 个点的竞赛图,其中 \(i\) 的入度为 \(k_i\),你可以进行交互,每次询问点对 \((A,B)\),回答是否存在 \(A\) 到 \(B\) 的路径,如果回答为 "Yes" 则不能再询问,否则可以继续询问。
求出一个点对 \((A,B)\) ,其中 \(A,B\) 可以互相到达,并且 \(|k_A-k_B|\) 最大。
\(3\leq n\leq 500\)。
\(\mathcal{Solution}\)
提供一个 \(\mathcal{O}(n)\) 且不需要询问的做法。
简单来说是:按照入度从小到大排序,如果到前 \(i\) 个的入度和恰好为 \(i\times(i-1)/2\),则出现了一个新的强连通分量,假设上一次符合条件的是 \(lst\),则 \([lst+1,i]\) 构成了一个新的强连通分量。
这样遍历一遍就可以求出每个强连通分量里有哪些点,就能统计出答案了。
排序选择计数排序,时间复杂度 \(\mathcal{O}(n)\)。
\(\mathcal{Code}\)
//Code by do_while_true
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#define pp std::pair<int,int>
#define mp std::make_pair
#define fir first
#define sec second
template <typename T>
T& read(T& r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
const int N = 510;
int n, mx = -1, lst, sum, ansu, ansv, ct;
std::vector<int>vec[N];
pp a[N];
signed main() {
read(n);
for(int i = 1, x; i <= n; ++i) {
read(x);
vec[x].push_back(i);
}
for(int i = 0; i <= n; ++i)
for(auto j : vec[i])
a[++ct] = mp(i, j);
for(int i = 1; i <= n; ++i) {
sum += a[i].fir;
if(sum == i * (i-1) / 2) {
if(lst != i-1) {
int now = a[i].fir - a[lst+1].fir;
if(now > mx)
mx = now,
ansu = a[lst+1].sec,
ansv = a[i].sec;
}
lst = i;
}
}
printf("! %d %d\n", ansu, ansv);
return 0;
}
\(\mathcal{Proof}\)
竞赛图有个经典结论,其强连通缩点后的DAG呈类似于链状, 前面的所有点向后面的所有点连边。网上能搜到很多证明,这里就不重复论述了。
可以发现,拓扑序在前的SCC的任意一节点的入度严格小于拓扑序在后的SCC的任意一节点入度。因为前面的SCC的点必定向后面的SCC的点连边。
所以所有节点按照入度和从小到大排序后,同一个SCC的节点一定是连续的。
引理一:若一个竞赛图按照入度从小到大排序,仅有 \(i=n\) 满足前 \(i\) 的入度和为 \(i\times (i-1)/2\),则这个竞赛图缩点后只有一个SCC。
原因很简单,如果有多个SCC,那么第一个SCC若以 \(j\) 结尾,一定满足入度和为 \(j\times(j-1)/2\),与仅有 \(i=n\) 时满足不符。
\(\mathcal{Solution}\) 中的结论的必要性比较显然,在此不多赘述。
考虑 \(Solution\) 中提到的步骤,由引理一得,若第一个满足条件的为 \(i\) ,则前 \(i\) 个节点在同一个SCC,因为前 \(i\) 个节点之间互相只有 \(i\times(i-1)/2\) 条边,而入度也正好统计到了这么多边,所以可以看成引理一的情况。
现在已经求出第一个SCC了,如果我们找到的第二个满足条件的点在 \(j\) 。
由于满足必要性,所以此时考虑前 \(j\) 个节点时,要不然是两个 SCC,要不然后面的组成不了SCC。
设第一个SCC为集合 \(L\),其大小为 \(l\)。后面的点组成的集合为 \(R\),其大小为 \(r\)。
只考虑 \(L\),则他们的入度和为 \(\sum\limits_{k=0}^{l-1}k=l\times(l-1)/2\),由于图为竞赛图,所以 \(L\) 中的每个点一定都与 \(R\) 中每个点相连,且这些有向边一定是朝向 \(R\) 中的点,这一部分的入度和为 \(l\times r=\sum\limits_{k=l}^{j-1}l\)。
总的入度和为 \(j\times(j-1)/2=\sum\limits_{k=0}^{j-1}k\),发现这个求和与前面两个求和的差值恰好为 \(\sum\limits_{k=l}^{j-1}k-l=\sum\limits_{k=0}^{r-1}k\)。
也就是说,\(R\) 内部的点的入度和为 \(\sum\limits_{k=0}^{r-1}k=r\times(r-1)/2\),由引理一可得 \(R\) 可组成一个SCC。
这是两个的情况,注意到对于 \(L\) 来说,仅使用到了 \(L\) 内部的入度和为 \(l\times(l-1)/2\) ,以及 \(L\) 中的每个点一定都与 \(R\) 中每个点相连,将 \(L\) 扩展为前 \(k\) 个SCC的点组成的集合同样满足这两条性质。
结论的充分性得证。
本文即使略去一些繁琐的,不必要的推导,仍显得篇幅略长。如有质疑或建议欢迎提出。