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;
}

猜你喜欢

转载自www.cnblogs.com/mangoyang/p/10436264.html
今日推荐