[CF1713D]Tournament Countdown 题解
作者:互联网
(注:写题解时 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\),然后再对这两个局部的胜利者进行一次询问。
继续写下去,正解就呼之欲出了。
-
首先特判 \(n=1\) 的情况,直接用一次询问得出答案。
-
若 \(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