zoukankan      html  css  js  c++  java
  • LG 题解 P7843 「PMOI-4」猜排列

    写在前面

    感谢 @zimujunqwq 的思路。

    Description

    题目描述
    不知如何简化题意

    Solution

    Subtask1

    考虑用询问 (2)(3, 4) 的元素位置确定,那么剩下的 (1,2) 的两个位置也能确定。

    注意我们暂时还不能区分他们。

    此时利用询问 (1),询问 (3,4) 两个位置对 (1,2) 两个位置取模的结果。

    他们的结果是不同的,所以可以确定出 (1,2,3,4) 对应的位置了。

    期望得分 (10pts)

    Subtask2

    通过观察数据范围发现 (m_2^2 = m_3)

    所以注定这是一个 (O(n^2)) 算法

    我们可以利用 (2) 操作询问 (n) 次。把 (p)(n) 不断下调,即每次询问 (p) 都比上一次小 (1)

    这样每次询问我们都会多知道一个元素的位置,而这个元素的值也是确定的。

    期望得分 (10 pts)

    Subtask3,4,5

    反正我没看出 Subtask3,4 有什么单独的做法,有卡到这一档的可以单独和我说一声。

    注意到后面的数据 (n = m_1),所以我们可能每次询问 (1) 都要确定一个位置。

    现在假设我们知道了 (left lceil frac{n}{2} ight ceil) 的位置,那么不难发现, (left lceil frac{n}{2} ight ceil + 1 sim n) 这些数对 (left lceil frac{n}{2} ight ceil) 取模的值是互不相同的。这就启发我们可以先确定出 (left lceil frac{n}{2} ight ceil) 的位置,然后通过几次询问确定他后面的数的位置。注意模数为 (0) 时要特判一下。

    这样的话,问题的规模就会缩小一半。

    我们就可以继续处理剩下一半的情况。

    如何确定 (left lceil frac{n}{2} ight ceil) 的位置?

    可以询问两次询问 (2) 。第一次求出 (>left lceil frac{n}{2} ight ceil) 的所有位置,第二次求出 (ge left lceil frac{n}{2} ight ceil) 的所有位置,那么新增加的位置就是 (left lceil frac{n}{2} ight ceil)

    我们可以发现,这样递归处理需要 (log n) 次,并且数据范围是 (2 log n le m_2)

    期望得分 (50pts)

    Subtask6

    观察数据发现 (2^{17} ge 5 imes 10^4)

    那就是说只让我们进行 (log n) 次询问。

    我们考虑通过询问 (1)(left lceil frac{n}{2} ight ceil) 的位置求出来。我们想知道他的位置可以通过知道 (left lceil frac{n}{4} ight ceil) 的位置求出来。然后我们不断这样递归下去,发现我们只要知道了 (1) 的位置,并且知道了各个段的位置,就可以回溯到整个序列。而 (1) 的位置会在最后我们通过询问 (2) 询问 (p=1,2) 的时候求出。

    这样的话,我们就可以在 (log n) 次询问 (2) 内求出所有位置。

    期望得分 (25pts)

    Subtask7

    这个之后我们发现 (2^{15} le 5 imes 10^4)

    怎么压缩这两次询问?

    通过自己手模每次询问的位置发现最后几个询问是 (1,2,3,5),我们考虑把前两个询问去掉。

    最后询问 (3) 的时候,我们得到的两个位置一定是 (3,4),未知的两个位置是 (1,2)

    这是什么?Subtask1 啊!

    然后此题完结。

    需要注意的一点是,Subtask3,4 的数据下,最后几次询问的位置是 (1,2,3,4,6),也就是说 (3,4) 没有被划分到一段内。这个情况更好处理 (1,2) 因为我们知道了 (3) 的位置。

    我们要爱惜自己的询问次数,因为 (n = m_1),不能浪费,所以确定出前几个数后,再求其他数的时候要从已经确定的数的下一个开始求。

    代码实现

    我用 (col) 按照第几次询问标记了每个段的颜色,然后利用一个 (c) 数组将颜色从大到小排序,这样每段颜色都被放在了一起,颜色相同的段在值域上也是连续的,就可以 (O(n)) 的求出整个序列了。

    实现细节参考代码吧。

    Code

    /*
    Work by: Suzt_ilymics
    Problem: 不知名屑题
    Knowledge: 垃圾算法
    Time: O(能过)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #define LL long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 1e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n, m1, m2, m3;
    int a, b, x, y, mod1, mod2;
    int ans[MAXN];
    int vis[MAXN], c[MAXN];
    int stc[MAXN], sc = 0;
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    namespace Subtask1 {
        void Solve() {
            printf("? 4 1 2 3 4 3
    ");
            fflush(stdout);
            int k = read();
            x = read(), y = read();
            for(int i = 1; i <= 4; ++i) if(i != x && i != y) a = i;
            for(int i = 1; i <= 4; ++i) if(i != x && i != y && i != a) b = i;
            printf("! %d %d
    ", x, a);
            fflush(stdout);
            mod1 = read();
            printf("! %d %d
    ", x, b);
            fflush(stdout);
            mod2 = read();
            if(mod1 + mod2 == 0) {
                ans[x] = 4, ans[y] = 3;
            } else {
                ans[x] = 3, ans[y] = 4;
                if(mod1 == 1) {
                    ans[a] = 2, ans[b] = 1;
                } else {
                    ans[b] = 2, ans[a] = 1;
                }
                printf("A %d %d %d %d
    ", ans[1], ans[2], ans[3], ans[4]);
                fflush(stdout);
                return ;
            }
            
            printf("! %d %d
    ", y, a);
            fflush(stdout);
            mod1 = read();
            printf("! %d %d
    ", y, b);
            fflush(stdout);
            mod2 = read();
            if(mod1 == 1) {
                ans[a] = 2, ans[b] = 1;
            } else {
                ans[b] = 2, ans[a] = 1;
            }
            printf("A %d %d %d %d
    ", ans[1], ans[2], ans[3], ans[4]);
            fflush(stdout);
            return ;
        }
    }
    namespace Subtask2 {
        void Solve() {
            for(int i = n; i >= 1; --i) {
                printf("? %d ", n);
                for(int j = 1; j <= n; ++j) printf("%d ", j);
                printf("%d
    ", i);
                fflush(stdout);
                int k = read();
                for(int j = 1, x; j <= k; ++j) {
                    x = read();
                    if(vis[x]) continue;
                    vis[x] = true;
                    ans[x] = i;
                }
            }
            printf("A");
            for(int i = 1; i <= n; ++i) {
                printf(" %d", ans[i]);
            }
            puts("");
            fflush(stdout);
        }
    }
    
    bool cmp(int x, int y) { return vis[x] > vis[y]; }
    
    void Divide(int l, int r, int col) {
        int mid = (r + 1) / 2 + 1;
        if(mid == 2) {
            for(int i = 1; i <= n; ++i) if(!vis[i]) vis[i] = col;
            sort(c + 1, c + n + 1, cmp);
            return ;
        }
        sc = 0;
        for(int i = 1; i <= n; ++i) {
            if(!vis[i]) stc[++sc] = i;
        }
        printf("? %d ", sc);
        for(int i = 1; i <= sc; ++i) printf("%d ", stc[i]);
        printf("%d
    ", mid);
        fflush(stdout);
        int k = read();
        for(int i = 1, x; i <= k; ++i) {
            x = read();
            vis[x] = col;
        }
        Divide(l, mid - 1, col + 1);
    }
    
    int main()
    {
        n = read(), m1 = read(), m2 = read(), m3 = read();
        if(n == 4) {
            Subtask1::Solve();
        } else if(n == 500){
            Subtask2::Solve();
        } else {
            for(int i = 1; i <= n; ++i) c[i] = i;
            Divide(1, n, 1);
    //        for(int i = 1; i <= n; ++i) cout<<vis[i]<<" "; puts("");
    //        for(int i = 1; i <= n; ++i) cout<<c[i]<<" "; puts("");
            int Max, lst, wz;
            if(vis[c[3]] != vis[c[4]]) {
                printf("! %d %d
    ", c[3], c[1]);
                fflush(stdout);
                int mod1 = read();
                printf("! %d %d
    ", c[3], c[2]);
                fflush(stdout);
                int mod2 = read();
                ans[c[3]] = 3;
                if(mod1 == 1) ans[c[1]] = 2, ans[c[2]] = 1;
                else ans[c[1]] = 1, ans[c[2]] = 2;
                Max = 3, lst = wz = c[3];
            } else {
                printf("! %d %d
    ", c[3], c[1]);
                fflush(stdout);
                int mod1 = read();
                printf("! %d %d
    ", c[3], c[2]);
                fflush(stdout);
                int mod2 = read();
                if(mod1 + mod2 == 0) {
                    ans[c[3]] = 4, ans[c[4]] = 3;
                    printf("! %d %d
    ", c[4], c[1]);
                    fflush(stdout);
                    mod1 = read();
                    if(mod1 == 1) ans[c[1]] = 2, ans[c[2]] = 1;
                    else ans[c[1]] = 1, ans[c[2]] = 2;
                } else {
                    ans[c[3]] = 3, ans[c[4]] = 4;
                    if(mod1 == 1) ans[c[1]] = 2, ans[c[2]] = 1;
                    else ans[c[1]] = 1, ans[c[2]] = 2;
                }
                Max = 4, lst = wz = (ans[c[4]] == 4 ? c[4] : c[3]);
            }
            for(int i = ((vis[c[3]] == vis[c[4]]) ? 5 : 4); i <= n; ++i) {
                if(vis[c[i]] != vis[c[i - 1]]) {
                    lst = wz; // 上一个颜色 
                    Max = 0, wz = 0; // 新的颜色 
                }
                printf("! %d %d
    ", c[i], lst);
                fflush(stdout);
                int x = read();
                if(x) {
                    ans[c[i]] = ans[lst] + x;
                } else {
                    ans[c[i]] = ans[lst] * 2;
                }
                if(Max < ans[c[i]]) {
                    Max = ans[c[i]];
                    wz = c[i];
                }
            }
            printf("A");
            for(int i = 1; i <= n; ++i) {
                printf(" %d", ans[i]);
            }
            puts("");
            fflush(stdout);
        }
        return 0;
    }
    
  • 相关阅读:
    LeetCode 227. Basic Calculator II
    LeetCode 224. Basic Calculator
    LeetCode 103. Binary Tree Zigzag Level Order Traversal
    LeetCode 102. Binary Tree Level Order Traversal
    LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode 169. Majority Element
    LeetCode 145. Binary Tree Postorder Traversal
    LeetCode 94. Binary Tree Inorder Traversal
    LeetCode 144. Binary Tree Preorder Traversal
  • 原文地址:https://www.cnblogs.com/Silymtics/p/15168814.html
Copyright © 2011-2022 走看看