Codeforces 1129 E.Legendary Tree
Codeforces 1129 E.Legendary Tree
解题思路:
这题好厉害,我来复读一下官方题解,顺便补充几句。
首先,可以通过询问 \(n-1\) 次 \((S=\{1\},T=\{2\dots n\},i),i\in[2,n]\) ,来得到以 \(1\) 为根树的所有节点的子树大小。
然后考虑从子树大小最小的节点开始,为每一个节点找它们的儿子,由于每个节点儿子的子树大小,一定小于这个节点的子树大小,所以可以按照子树大小从小到大排序,每个节点的儿子就一定在其前面。
假设已经找到了一个节点的儿子,那么考虑把这个节点当前找到的这个儿子在序列中删除,对于序列中在其后面的那个节点来说,其前面除了它的儿子以为所有点都不在它的子树中。
那么对于当前做到的序列的第 \(x\) 个位置,可以二分一个位置 \(mid\) ,询问 \((S=\{1\},T=\{a_1\dots a_{mid}\},x)\) ,第一个返回结果不是 \(0\) 的 \(mid\) 一定 \(x\) 的儿子,这样重复做下去直到还原总询问次数是 \(O(n-1+(n-1)logn)\)。
code
/*program by mangoyang*/
#pragma GCC optimize("inline", "Ofast")
#include <bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 1005;
vector<int> S, T;
vector<pair<int, int> > Edge;
int sz[N], a[N], del[N], n;
inline int query(vector<int> S, vector<int> T, int x){
printf("%d\n", (int) S.size());
for(int i = 0; i < (int) S.size(); i++)
printf("%d ", S[i]);
printf("\n%d\n", (int) T.size());
for(int i = 0; i < (int) T.size(); i++)
printf("%d ", T[i]);
printf("\n%d\n", x);
fflush(stdout);
int ans = 0;
return read(ans), ans;
}
inline bool cmp_size(int x, int y){
return sz[x] < sz[y];
}
inline int check(int mid, int x){
T.clear();
for(int i = 1; i <= mid; i++)
if(!del[i]) T.push_back(a[i]);
if(!(int)T.size()) return 0;
return query(S, T, x) > 0;
}
int main(){
read(n);
S.push_back(1);
for(int i = 2; i <= n; i++) T.push_back(i);
sz[1] = n;
for(int i = 2; i <= n; i++)
sz[i] = query(S, T, i);
for(int i = 1; i <= n; i++) a[i] = i;
sort(a + 1, a + n + 1, cmp_size);
for(int i = 1; i < n; i++){
int u = a[i];
if(sz[u] == 1) continue;
while(1){
int l = 1, r = i - 1, pos = -1;
while(l <= r){
int mid = (l + r) >> 1;
if(check(mid, u)) pos = mid, r = mid - 1;
else l = mid + 1;
}
if(pos == -1) break;
del[pos] = 1;
Edge.push_back(make_pair(u, a[pos]));
}
}
for(int i = 1; i < n; i++)
if(!del[i]) Edge.push_back(make_pair(a[i], 1));
puts("ANSWER");
for(int i = 0; i < (int) Edge.size(); i++)
printf("%d %d\n", Edge[i].first, Edge[i].second);
fflush(stdout);
return 0;
}