Loading

题解 CF1514E Baby Ehab's Hyper Apartment [*medium]

能在场上想出来自己不怎么擅长的交互题还是比较开心的(

但是因为不了解 Extra Registration(在 \(30\) 分钟之后就不能报名了),没能交上去。。。

题面

给定一张 \(n\) 个点竞赛图,你可以进行一些询问,询问有两种:

  • \(1\) \(a\) \(b\):查询是否有从 \(a\)\(b\) 的有向边。这种询问次数 \(\le 9n\)
  • \(2\) \(x\) \(k\) \(s_1\) \(s_2\) \(...\) \(s_k\) : 查询 \(x\)\(s_1\)\(s_2\),...,\(s_k\) 是否存在一条有向边。这种询问次数 \(\le 2n\)

最后要求输出对于所有 \((i, j)\),是否有 \(i\)\(j\)路径。多测。

题解

众所周知把竞赛图缩点之后形成的强联通分量是一条链,如果知道了这些的强联通分量就可以得出答案。

接下来分成两个步骤:

  1. 找到一条包括 \(1, 2, ..., n\) 的路径。

  2. 在这条路径上进行缩点。

步骤1

考虑增量,设这条链原先是 \(v_1, v_2, ..., v_k\),现在要加入点 \(k\)

因此要找出一个位置 \(p\)\(1 \le p \le k + 1\)) 满足有一条从 \(v_{p-1}\)\(k\) 的边且有一条从 \(k\)\(v_p\) 的边,并在 \(p\) 之前插入 \(k\)

如果有从 \(v_{l-1}\)\(k\) 的边且有从 \(k\)\(v_r\) 的边,那么可以在 \([l, r]\) 找到答案。

考虑二分答案,如果有 \(k\)\(v_{mid}\) 的边那么在 \([l,mid]\) 中可以找到答案,否则在 \([mid+1, r]\) 种可以找到答案。

需要 \(n \log n\) 个询问 \(1\)

步骤2

设目前缩出来的链是 \(S_1 \to S_2 \to ... \to S_k\)

从后往前缩点,对于一个 \(S_t\) 中的点 \(z\),如果存在一条从 \(S_1, S_2, ..., S_{t - 1}\) 的点到 \(z\) 的边(可以利用询问 \(2\) 查询),那么 \(S_t\)\(S_{t-1}\) 必然在一个集合中。

不停地这样缩点即可得到最终答案。需要 \(2n\) 个询问 \(2\)

代码

#include<bits/stdc++.h>
#define L(i, j, k) for(int i = j, i##E = k; i <= i##E; i++)
#define R(i, j, k) for(int i = j, i##E = k; i >= i##E; i--)
#define ll long long
#define pii pair<int, int>
#define db double
#define x first
#define y second
#define ull unsigned long long
#define sz(a) ((int) (a).size())
#define vi vector<int>
using namespace std;
const int N = 233;
int n, tot, v[N];
set < int > S[N];
void merge(int x, int y) {
	for(int k : S[y]) S[x].insert(k);
	S[y].clear();
}
int getin() {
	int k;
	return cin >> k, k;
}
bool check(int x, int y) {
	return cout << 1 << " " << x << " " << y << endl, getin();
}
bool all[N][N], vis[N];
int rmain() {
	memset(all, 0, sizeof(all)), memset(vis, 0, sizeof(vis)), memset(v, 0, sizeof(v));
	L(i, 1, tot) S[i].clear();
	tot = 0;
	cin >> n;
	L(i, 0, n - 1) {
		int l = 1, r = tot + 1;
		while(l < r) {
			int mid = (l + r) >> 1;
			if(mid == tot + 1 || check(i, v[mid])) r = mid;
			else l = mid + 1;
		}
		R(i, tot, l) v[i + 1] = v[i];
		++tot, v[l] = i;
	}
	L(i, 1, tot) S[i].insert(v[i]);
	int now = tot;
	while(now > 1) {
		for(int v : S[now]) if(!vis[v]) {
			cout << 2 << " " << v << " ";
			int cnt = 0;
			L(i, 1, now - 1) cnt += sz(S[i]);
			cout << cnt << " ";
			L(i, 1, now - 1) for(int u : S[i]) cout << u << " ";
			cout << endl;
			if(getin()) {
				merge(now - 1, now);
				break;	
			}
			vis[v] = true;
		}
		--now;
	}
	L(i, 1, tot) L(j, i, tot) for(int u : S[i]) for(int v : S[j]) all[u][v] = true;
	cout << 3 << endl;
	L(i, 0, n - 1) {
		L(j, 0, n - 1) cout << all[i][j];
		cout << endl;	
	} 
	return getin(), 0;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int T;
	cin >> T;
	while(T--) rmain();
	return 0;
} 

祝大家学习愉快!

posted @ 2021-04-20 09:58  zhoukangyang  阅读(335)  评论(0编辑  收藏  举报