首页 > 其他分享 >CF1526F Median Queries 题解

CF1526F Median Queries 题解

时间:2024-09-20 11:03:55浏览次数:1  
标签:std 器会 int 题解 Median CF1526F rnd leq 交互

Description

本题是一道交互题。

给定 \(n\),你需要猜测一个长度为 \(n\) 的排列 \(p\)(即 \(p\) 包含所有 \(1\) 到 \(n\) 的整数各一次)。已知 \(p\) 满足 \(p_1<p_2\)。

当然,你可以进行询问,每次询问你需要给定三个互不相同的整数 \(a,b,c\),交互器会返回 \(|p_a-p_b|,|p_b-p_c|,|p_c-p_a|\) 这三个数中第二小的数。

请在 \(2\times n+420\) 次询问内求出整个排列 \(p\)。\(T\) 组数据。

交互格式如下:

  • 首先你需要读入整数 \(T\) 表示数据组数。接下来对于每组数据:
  • 首先读入一个整数 \(n\) 表示排列长度。如果你希望询问,按照 ? a b c 的格式输出,你将收到 \(|p_a-p_b|,|p_b-p_c|,|p_c-p_a|\) 中第二小的数。你需要保证 \(1\leq a,b,c\leq n\),且 \(a,b,c\) 互不相同。请注意,如果你的询问不合法,或者你的询问次数过多,交互器会给出 -1 的结果,此时你需要立即停止程序,否则可能会得到任意的评测结果。如果你希望回答,按照 ! p_1 p_2 ... p_n 的格式输出。如果你的回答正确,交互器会给出 1 的结果,此时你需要直接进行下一组数据(如果有的话),否则交互器会给出 -1 的结果,同样的你需要立刻结束程序。

注意刷新缓冲区。

\(1\leq T\leq10^3,20\leq n,\sum n\leq10^5\)。

Solution

考虑怎么一一确定每个数。

注意到如果已经直到 \(1\) 和 \(2\) 或 \(n-1\) 和 \(n\) 的位置之后,再通过 \(n-2\) 次操作就能确定每个数了。

同时我们如果只知道边上的两个数,但不知道是 \(1,2\) 还是 \(n-1,n\),也是可以做的。因为可以先把边上的数当作 \(1,2\) 确定出整个排列,如果 \(p_1>p_2\) 则让 \(p_i\leftarrow n-p_i+1\),否则不变。

现在问题转化为怎么找到边上的两个数。

有个不显然的观察是对于一对距离不长的 \((a,b)\),与它们距离最大的两个点一定在边界上。结论是 \(b-a+1\leq\frac{n-4}{3}\) 时就是对的。

找到长度不超过 \(\frac{n-4}{3}\) 的点对是简单的,可以一直随,直到随到权值 \(\leq\frac{n-4}{6}\) 的 \((a,b,c)\) 即可。单次随到的概率是 \(\frac{1}{9}\),\(420\) 次一定能随到。

细节见代码。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5;

int n;
int p[kMaxN], val[kMaxN];
std::mt19937 rnd(std::random_device{}());

int ask(int x, int y, int z) {
  std::cout << "? " << x << ' ' << y << ' ' << z << std::endl;
  int d;
  std::cin >> d;
  return d;
}

std::tuple<int, int, int> gettuple() {
  int a = rnd() % n + 1, b = rnd() % n + 1, c = rnd() % n + 1;
  while (a == b || a == c || b == c) a = rnd() % n + 1, b = rnd() % n + 1, c = rnd() % n + 1;
  return {a, b, c};
}

std::pair<int, int> getpair() {
  int a, b, c;
  for (int c = 1; c <= 420; ++c) {
    std::tie(a, b, c) = gettuple();
    if (ask(a, b, c) <= (n - 4) / 6) return {a, b};
  }
}

void dickdreamer() {
  std::cin >> n;
  auto [a, b] = getpair();
  for (int i = 1; i <= n; ++i) {
    val[i] = 0;
    if (i != a && i != b) val[i] = ask(a, b, i);
  }
  int p1 = 0, p2 = 0;
  p1 = std::max_element(val + 1, val + 1 + n) - val;
  std::vector<int> vec;
  for (int i = 1; i <= n; ++i) {
    if (val[i] == val[p1] - 1)
      vec.emplace_back(i);
  }
  if ((int)vec.size() == 1) {
    p2 = vec[0];
  } else {
    assert(vec.size() == 2);
    if (ask(a, p1, vec[0]) < ask(a, p1, vec[1])) p2 = vec[0];
    else p2 = vec[1];
  }
  p[p1] = 1, p[p2] = 2;
  for (int i = 1; i <= n; ++i)
    if (i != p1 && i != p2)
      p[i] = ask(i, p1, p2) + 2;
  if (p[1] > p[2]) {
    for (int i = 1; i <= n; ++i)
      p[i] = n - p[i] + 1;
  }
  std::cout << "! ";
  for (int i = 1; i <= n; ++i) std::cout << p[i] << ' ';
  std::cout << std::endl;
  std::cin >> n;
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  // std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:std,器会,int,题解,Median,CF1526F,rnd,leq,交互
From: https://www.cnblogs.com/Scarab/p/18422071

相关文章

  • P9705 「TFOI R1」Unknown Graph题解
    思路题目给出了每个点的初度和入度,由于图是无重边无自环的有向图。要求满足限制的图有多少条边与建图方案。我们可以考虑使用网络流中的最大流。我们知道这是一道网络流后,题目的难点就转移到网络流的建模以及输出方案的办法。网络流的建模:题目所给的条件没有源点汇点并指出......
  • P5979 [PA2014] Druzyny 题解
    对于一个固定的右端点\(r\),左端点\(l\)合法当且仅当\(\max(d_l,d_{l+1},\dotsd_r)\ler-l+1\le\min(c_l,c_{l+1},\dots,c_r)\)。容易得到一个很朴素的dp:记\(f_i\)表示前\(i\)个位置可以分成的组的数目的最大值,\(g_i\)表示有多少种分组方案能达到最大值,直接枚举左端点......
  • CF2004G 题解
    题意定义关于数字串\(s\)的函数\(f(s)\)表示将\(t\)切分为\(m\)段,要求\(m\)是偶数,假设这些字符串分别为\(t_1,t_2,\ldots,t_m\),有\(s=t_1+t_2+\ldots+t_m\)。定义\(A^x\)表示\(\underbrace{AA\ldotsAA}_{x~\text{times}}\),求一种划分方式,使得\(t_2^......
  • P1108 低价购买 题解
    这题在求最长下降子序列的基础上加了一个求方案数的要求,这就让这道题目变难了很多。我们考虑我们在求最长下降子序列的时候,总是从这一位,要么重新开始计数,要么只和前面的有关,所以前面的信息完全丢失了,无法判断有多少方案,所以我们就要针对前面的方案数设计一个dp来统计。可以称之......
  • 题解:CF1906F Maximize The Value
    可以在cnblog中阅读。见这种题比较少,写篇题解加深印象。如果直接上数据结构维护数组,似乎没有好的办法处理操作序列的一个子段。那不妨转变思路,对操作序列上数据结构维护。假设顺序进行每个修改操作,我们用时间表示修改操作的编号,位置表示数组的下标,则常见的维护序列的数据结构......
  • 蓝桥杯十五届软件赛C++B组题解
    最近蓝桥杯官网已经把十五届题目上架了,我会尽快的将题解发出来,没有发的过段时间再补。​​​​​​​数字接龙一个很鹅心的搜索题,一不注意就会写错,比赛的时候写不来,题目上架后也WA了两个样例才过。题目大意:也就是说从(1,1)开始 ,下一步路的数据总是要比当前数据大1,超过k就......
  • P2051 [AHOI2009] 中国象棋 题解
    DP好题?首先确定,每一行/列只能放至多两个棋子,这么少,所以我们的状态肯定和棋子数有关。由于我们不关注具体的方案数,所以我们不妨只关心对应棋子数量的行/列的数量。同时,由于考虑行和列都是一样的,所以我们不妨用行递推。所以我们设$\dp_{i,j,k}\$表示当前放到第\(i\)行,有\(......
  • Capital许可使用常见问题解答
    在使用Capital软件的过程中,许多用户可能会遇到关于许可使用的各种问题。为了帮助大家更好地理解和合规使用Capital软件,我们整理了一份常见问题解答,希望能为您提供有价值的参考。一、Capital许可证的类型有哪些?Capital提供多种许可证类型,包括永久许可证、订阅许可证等。永久许可......
  • 【题解】Solution Set - NOIP2024集训Day32 数位 dp
    【题解】SolutionSet-NOIP2024集训Day32数位dphttps://www.becoder.com.cn/contest/5537order:1,3,5,6,2,4「SDOI2013」淘金就是要算前\(k\)大的和。考虑一个位置\((i,j)\)在变化完了之后的金子个数。(也即逆变换。设:\(f^\prime(x)\)表示在\(1\simN\)范围内,数位......
  • 【洛谷 P5730】【深基5.例10】显示屏 题解(数组+循环)
    【深基5.例10】显示屏题目描述液晶屏上,每个阿拉伯数字都是可以显示成的点阵的(其中X表示亮点,.表示暗点)。现在给出数字位数(不超过)和一串数字,要求输出这些数字在显示屏上的效果。数字的显示方式如同样例输出,注意每个数字之间都有一列间隔。输入格式第一行输入一个正整数,表示数......