ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LG题解 P7843 「PMOI-4」猜排列

2021-08-21 09:02:36  阅读:248  来源: 互联网

标签:LG lceil right frac 题解 询问 位置 P7843 rceil


写在前面

感谢 @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} \right \rceil\) 的位置,那么不难发现, \(\left \lceil \frac{n}{2} \right \rceil + 1 \sim n\) 这些数对 \(\left \lceil \frac{n}{2} \right \rceil\) 取模的值是互不相同的。这就启发我们可以先确定出 \(\left \lceil \frac{n}{2} \right \rceil\) 的位置,然后通过几次询问确定他后面的数的位置。注意模数为 \(0\) 时要特判一下。

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

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

如何确定 \(\left \lceil \frac{n}{2} \right \rceil\) 的位置?

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

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

期望得分 \(50pts\)

Subtask6

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

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

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

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

期望得分 \(25pts\)。

Subtask7

这个之后我们发现 \(2^{15} \le 5 \times 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\n");
        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\n", x, a);
        fflush(stdout);
        mod1 = read();
        printf("! %d %d\n", 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\n", ans[1], ans[2], ans[3], ans[4]);
            fflush(stdout);
            return ;
        }
        
        printf("! %d %d\n", y, a);
        fflush(stdout);
        mod1 = read();
        printf("! %d %d\n", 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\n", 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\n", 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\n", 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\n", c[3], c[1]);
            fflush(stdout);
            int mod1 = read();
            printf("! %d %d\n", 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\n", c[3], c[1]);
            fflush(stdout);
            int mod1 = read();
            printf("! %d %d\n", c[3], c[2]);
            fflush(stdout);
            int mod2 = read();
            if(mod1 + mod2 == 0) {
                ans[c[3]] = 4, ans[c[4]] = 3;
                printf("! %d %d\n", 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\n", 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;
}

标签:LG,lceil,right,frac,题解,询问,位置,P7843,rceil
来源: https://www.cnblogs.com/Silymtics/p/15168814.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有