其他分享
首页 > 其他分享> > [CF1713D]Tournament Countdown 题解

[CF1713D]Tournament Countdown 题解

作者:互联网

传送门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\),对交互结果进行分类讨论:

综上,\(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