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;
    }
    
  • 相关阅读:
    jvm参数陷阱
    concurrent mode failure
    17.Quick QML-SpinBox
    16.Quick QML-ButtonGroup、RadioButton、CheckBox
    15.Quick QML-TextEdit和TextArea
    14.Quick QML-TextInput和TextField详解
    13.Quick QML-RowLayout、ColumnLayout、GridLayout布局管理器介绍、并通过GridLayout设计的简易网站导航界面
    9.qml-property自定义属性
    macOS 系统安装Maven教程
    macOS系统上 为Github 托管项目的访问添加SSH keys
  • 原文地址:https://www.cnblogs.com/Silymtics/p/15168814.html
Copyright © 2011-2022 走看看