ICode9

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

[CF1713D]Tournament Countdown 题解

2022-08-07 02:02:49  阅读:213  来源: 互联网

标签:const int 题解 询问 long Countdown 次数 CF1713D define


传送门QAQ

(注:写题解时 system test 还未进行,如果 fst 了就看个乐吧 qwq)

\(2^n\) 个人打淘汰赛。\(1\) 号和 \(2\) 号打,\(3\) 号和 \(4\) 号打,依次类推。

胜利的 \(2^{n-1}\) 人再这样打下去,直到唯一的胜者出现。

你不知道比赛的具体情况,请用不超过 \(\lceil \frac{2^{n+1}}{3} \rceil\) 次询问得出胜者,询问格式如下:

  • ? a b,表示询问 \(a\) 号和 \(b\) 号胜利次数的关系。\(1\) 表示 \(a\) 赢的次数比 \(b\) 多,\(2\) 表示 \(a\) 赢的次数比 \(b\) 少,\(0\) 表示 \(a,b\) 胜利次数相同。

\(1\le n\le 17\)。

Preface

为了方便,设 \(i\) 胜利次数为 \(c_i\)。

发现题中要求的其实就是 \(c\) 值最大的人,显然最暴力的解法是直接进行 \(2^n-1\) 次询问得出答案。

但想一想会发现,这些询问里有很多是不必要的,我们可以通过已知的条件推断出来。

先推导简单的情况再进行推广,先令 \(n=2\),发现其实只需要 \(2\) 次询问就能找出答案,推导过程如下:

首先,询问 \(1 \ 3\),对交互结果进行分类讨论:

  • 结果为 \(1\):也就是 \(c_1\gt c_3= 0\),说明 \(c_1 = 1\),也就是说 \(1\) 和 \(2\) 的比赛中 \(1\) 一定胜利了,否则 \(c_1\) 不可能为 \(1\)。那 \(2\) 就没有询问的必要了,直接询问下 \(1\ 4\) 即可。

  • 结果为 \(2\):和上面类似。

  • 结果为 \(0\):即 \(c_1=c_3=0\),那就可以推出来 \(c_2=c_4=1\),询问 \(2\ 4\) 即可。

综上,\(n=2\) 的时候只需 \(2\) 次询问。

考虑推广,\(n=3\) 的时候怎么办?

其实就是拆成两个 \(n=2\),然后再对这两个局部的胜利者进行一次询问。

继续写下去,正解就呼之欲出了。

  1. 首先特判 \(n=1\) 的情况,直接用一次询问得出答案。

  2. 若 \(n \gt 1\),就把 \(2^n\) 拆成 \(2^{n-2}\) 个 \(4\) 人小组,用我们上面的算法判断,得出 \(2^{n-2}\) 个胜者,再递归到 \(n-2\) 的情况求解。

分析一下询问次数(这个我并不是太懂,如果有误请大佬指出):

原来 \(4\) 个数中求最大值需要 \(3\) 次询问,现在变成了 \(2\) 次,那么询问次数就是 \(\frac{2}{3}\times 2^{n-2}=\frac{2^{n-1}}{3}\) 次,可以通过。

(老觉得这个式子很怪,可能有问题,请大佬们帮忙看看 QAQ)

Code

// Problem: D. Tournament Countdown
// Contest: Codeforces - Codeforces Round #812 (Div. 2)
// URL: https://codeforces.com/contest/1713/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define mp make_pair
#define fir first
#define sec second
#define Chtholly set<node>::iterator
#define SET set<int>::iterator
#define VEC vector<int>::iterator
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int maxn = 5e5 + 5;
const int maxm = 3e6 + 5;
const int maxk = 30;
const ll mod = 1e9 + 7;
const int INF = 1e9;
const ll INFll = 1e16;
int n;
std::vector<int> s;
int ask(int a,int b) {
    int p;
    printf("? %d %d\n",a,b);
    fflush(stdout);
    scanf("%d",&p);
    return p;
}
void solve(int n) {
    if(n <= 1) {
        printf("! %d\n",ask(s[0] , s[1]) == 1 ? s[0] : s[1]);
        fflush(stdout);
        return ;
    }
    else if(n == 2) {
        int p = ask(s[0] , s[3]);
        if(p == 1) {
            printf("! %d\n",ask(s[0] , s[2]) == 1 ? s[0] : s[2]);
            fflush(stdout);
        }
        else if(p == 2) {
            printf("! %d\n",ask(s[1] , s[3]) == 1 ? s[1] : s[3]);
            fflush(stdout);
        }
        else {
            printf("! %d\n",ask(s[1] , s[2]) == 1 ? s[1] : s[2]);
            fflush(stdout);
        }
        return ;
    }
    vector<int> G;
    for(int i = 0;i < (1 << n);i += 4) {
        int p = ask(s[i] , s[i + 2]);
        if(p == 1) {
            G.pb(ask(s[i] , s[i + 3]) == 1 ? s[i] : s[i + 3]);
        }
        else if(p == 2) {
            G.pb(ask(s[i + 2] , s[i + 1]) == 1 ? s[i + 2] : s[i + 1]);
        }
        else {
            G.pb(ask(s[i + 1] , s[i + 3]) == 1 ? s[i + 1] : s[i + 3]);
        }
    }
    s = G;
    solve(n - 2);
    return ;
}
void work() {
    scanf("%d",&n);
    s.clear();
    for(int i = 1;i <= (1 << n);++ i)s.pb(i);
    solve(n);
    return ;
}
int main() {
    int T;
    scanf("%d",&T);
    while(T --)work();
    return 0;
}

完结撒花✿✿ヽ(°▽°)ノ✿

标签:const,int,题解,询问,long,Countdown,次数,CF1713D,define
来源: https://www.cnblogs.com/663B/p/16558337.html

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

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

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

ICode9版权所有