zoukankan      html  css  js  c++  java
  • AT2348 [ARC070D] HonestOrUnkind

    不妨先从无解的情况下手,不难发现当 (A le B) 时是一定无解的。

    因为不诚实的 (B) 个人可以装作是诚实的,全部说自己这一方是诚实的对方是不诚实的我们就无法判断了。

    下面我们就可以在 (A > B) 的情况下来处理这个交互问题了。

    一个最基本最靠谱的方法是先找到一个诚实的人,然后用他把所有人全部问一遍。

    因此下面我们的目的就是在 (n) 次询问以内找到一个诚实的人。

    首先需要一个基于操作的观察:

    若回答为 (F) 则两者之中必然存在一个人不诚实。

    那么我们可以考虑使用这一条性质,将不诚实的人排除开来这样剩下的就是诚实的人了。

    于此同时,因为我们只知道两者其一必不诚实,因此我们只能一次将两人一起排除。

    并且,由于一次排除至多排除一个诚实的人,又 (A > B) 所以最终剩下的人一定是诚实的。

    那么接下来的目的就是在 (n) 次内快速找到 (B) 组回答为 (F) 的人。

    首先不难发现一个查询次数 (n ^ 2) 的暴力,每次取出一个没有被排除的人,用所有人询问他一次,正确性显然。

    但是给予我们的询问上限是 (n) 次,但同时因为每个点至少要被询问一次,因为至少要保证每个不诚实的人都被至少查询一次(因为问不诚实的人的答案是不确定的)。

    同时上限是 (n),所以我们只能让每个点被恰好被询问一次。

    考虑用每个点恰好被询问一次来优化上面哪个暴力的做法。

    首先还是先取出一个人来,用另一个人来询问他,如果询问结果为 (F) 那么把两人删去,继续递归这个操作;否则我们只能选择让这两个人都留下来,因为无法确定两者中是否存在不诚实的人,再往下递归。

    同时因为每个点只能被查询一次,因此递归的时候只能让后面的人查询之前的查询者。

    那么最终必然剩下的就是一条查询链,其中每个查询答案均为 (T)

    再来观察一下这条查询链,其中必然存在至少一个诚实的人,因为弹出次数最多为 (B) 显然 (A > B) 所以必然至少有一个诚实的人。

    同时,你会发现这个查询链的最前端必然是诚实的人。

    否则找到最靠前的哪个诚实的人,他必然能带走之前哪个不诚实的人并一起弹走。

    这样我们就在 (n) 次查询内找到了一个诚实的人。

    可以发现的是上面的这个做法本质上就是一个弹栈和入栈的过程,用栈实现即可。

    复杂度 (O(n))

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, l, r) for (int i = l; i <= r; ++i)
    const int N = 4000 + 5;
    char s[5];
    int n, A, B, top, st[N], ans[N];
    int read() {
        char c; int x = 0, f = 1;
        c = getchar();
        while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
        while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    int main() {
        A = read(), B = read(), n = A + B;
        if(A <= B) { puts("Impossible"); return 0;}
        rep(i, 1, n) {
            if(!top) st[++top] = i;
            else {
                printf("? %d %d
    ", i - 1, st[top] - 1), cout.flush();
                scanf("%s", s + 1);
                if(s[1] == 'Y') st[++top] = i;
                else --top;
            }
        }
        rep(i, 1, n) {
            printf("? %d %d
    ", st[1] - 1, i - 1), cout.flush();
            scanf("%s", s + 1);
            ans[i] = (s[1] == 'Y');
        }
        printf("! ");
        rep(i, 1, n) printf("%d", ans[i]);
        return 0;
    }
    
  • 相关阅读:
    序列操作
    上帝造题的七分钟2 / 花神游历各国
    火柴排队
    pair(对组)用法
    线段树
    链上分治
    Rem与Px的转换
    css中单位px和em,rem的区别
    css网页自适应-1
    css网页自适应-2
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13852984.html
Copyright © 2011-2022 走看看