Codeforces 1514E Baby Ehab's Hyper Apartment

官方题解的操作好炫酷啊……

因为我们能得知的关于边的信息非常有限,所以考虑求出一条「通用」的路径,使得仅通过这些极少的路径就可以完成整张图的连通性的判断。

下面记题目中给出的两种询问分别为 $\text{OneEdge}(u, v)$ 和 $\text{ManyEdges}(u, S)$。

自然的,我们将目光投向这张竞赛图的哈密顿路径上。

考虑归并。比如我们现在在找 $[l, r]$ 这些点的哈密顿路径,那么可以分为两个部分 $[l, mid]$ 和 $[mid +1,r]$,递归求解这两部分的哈密顿路径。

比如 $[l, mid]$ 的哈密顿路径的起点是 $u$,$[mid+1, r]$ 的哈密顿路径的起点是 $v$,那么我们调用 $\text{OneEdge}(u, v)$,如果边是 $u \to v$ 的,那么我们就把 $u$ 放在 $[l, r]$ 的哈密顿路径的第一个,否则把 $v$ 放在第一个。

上面这么做为什么可行呢?

比如边是 $u \to v$ 的,那么当把 $u$ 放在第一位后,无论下一位是 $v$ 还是 $[l, mid]$ 中 $u$ 的后继,$u$ 都有一条直接连向后面的边,所以哈密顿路径的性质不会被破坏。

第一位确定以后,我们再继续按照上面的方法去确定第二位、第三位……即可。这个过程本质上就是对两个有序数组的归并。

当然,其实完全可以不用手写这个归并过程,我们可以调用 STL 的 stable_sort,而 $\text{OneEdge}$ 恰好就是 Compare 函数!

也就是说,我们先构造一个数组 $path_i = i$,接下来调用 stable_sort(path + 1, path + n + 1, OneEdge),得到的 $path_1 \to path_2 \to \cdots \to path_n$ 就是图中的一条哈密顿路径了。

上述调用 $\text{OneEdge}$ 的次数约 $n \log n$。

求出哈密顿路径后,显然所有 $path_i \to path_j (i < j)$ 的边就没有意义了,故只需要考虑反向边

于是我们逆序遍历 $path$ 数组,同时维护一下当前通过反向边最远能追溯到哪里,其实也就是维护一个指针 $p$,只要 $\text{ManyEdges}(path_{now}, \{path_1, path_2, \cdots, path_p\}) = 1$,就说明至少可以追溯到前 $p$ 位。

因为 $now$ 和 $p$ 都是单调下降的,所以上述过程是线性的,调用 $\text{ManyEdges}$ 的次数约为 $2n$。

于是这个问题就很好地得到了解决,代码非常短。

#include <bits/stdc++.h>
using namespace std;
int n, t, ret;
bool ans[105][105];
vector<int> path;
inline bool ManyEdges(const int &x, int len) // len 表示询问的 S 是 path[0] ~ path[len]
{
	if(len < 0) return false;
	cout << "2 " << x << ' ' << len + 1 << ' ';
	for(unsigned i = 0; i <= len; i++) cout << path[i] << ' ';
	cout << endl;
	cin >> ret;
	return ret;
}
inline bool OneEdge(const int &a, const int &b)
{
	cout << "1 " << a << ' ' << b << endl;
	cin >> ret;
	return ret;
}
int main()
{
	ios::sync_with_stdio(false);
	cin >> t;
	while(t--)
	{
		cin >> n;
		memset(ans, true, sizeof(ans));
		path.clear();
		for(int i = 0; i < n; i++) path.push_back(i);
		stable_sort(path.begin(), path.end(), OneEdge);
		int far = n - 2;
		for(int i = n - 1; ~i; i--)
		{
			if(far == i)
			{
				for(int j = 0; j <= i; j++) for(int k = i + 1; k < n; k++) ans[path[k]][path[j]] = false;
				far--;
			}
			while(ManyEdges(path[i], far)) far--;
		}
		cout << "3\n";
		for(int i = 0; i < n; i++, cout << endl) for(int j = 0; j < n; j++) cout << ans[i][j];
		cin >> n;
	}
}
posted @ 2021-04-22 13:12  syksykCCC  阅读(99)  评论(0编辑  收藏  举报