首页 > 其他分享 >20240924 模拟赛 T4 题解

20240924 模拟赛 T4 题解

时间:2024-09-24 22:50:04浏览次数:11  
标签:int 题解 T4 mid kMaxN leq 20240924 深度 询问

Description

这是一道交互题。
有一棵 \(n\) 个节点的树,现在要求你通过若干次询问得到这棵树的每一条边连接哪两个点。

每次询问你需要指定 \(n\) 个整数 \(d_1,d_2,\ldots,d_n\),满足 \(-1\leq d_i\leq n\),其中 \(1\leq i\leq n\)。

每次询问交互库会返回给你一个长度为 \(n\) 的布尔数组,第 \(i\) 个元素为 \(1\) 当且仅当存在某个点 \(j\) 满足 \(i,j\) 的树上距离 \(\leq d_j\),其中 \(1\leq i,j\leq n\)。

定义两点的树上距离为这两点的最短路径所经过的边数。

对于所有非样例的测试点,\(n=16000\),输入的边组成一棵 \(n\) 个点的树。

子任务编号 测试点数目 标准询问次数 特殊性质
\(0\) \(3\) \(1\) 样例
\(1\) \(30\) \(300\) 树随机生成
\(2\) \(37\) \(80\)

Solution

先考虑树随机生成怎么做。

容易发现这题的随机生成方式会让树的高度为 \(O(\log n)\),所以可以先钦定根,然后用 \(O(\log n)\) 次操作确定每个点的深度。

然后考虑怎么求出每个深度内每个点的父亲。

可以对于每个二进制位考虑,具体的,先选择一个深度 \(dep\),同时枚举二进制位 \(b\),然后把 \(dep\) 这层的点中第 \(b\) 位为 \(1\) 的树的 \(d\) 设成 \(1\) 跑一遍询问,这样回答中被选中的深度在 \(dep+1\) 的点的父亲一定满足第 \(b\) 位为 \(1\)。于是就可以单次 \(\left\lceil\log_2n\right\rceil\) 次操作确定一个层所有点的父亲。这样总次数为 \(O(\log^2 n)\)。

注意到上面那个东西可以让模 \(3\) 余数相等的层同时做,因为它们互不影响,于是可以做到去掉求每个点深度后 \(3\left\lceil\log_2n\right\rceil\) 次询问。

当树不是随机生成时,树的深度可能很大,\(O(dep)\) 的去求每个点的深度显然不可行。

考虑分治。假设当前分治区间为 \([l,r]\),并且已经分别知道了深度为 \([l,l]\) 和 \([l+1,r]\) 的所有点。

设 \(mid=\left\lceil\frac{l+r}{2}\right\rceil\)。可以对于所有深度为 \(l\) 的点分别做长度为 \(mid-l\) 和 \(mid-l-1\) 的询问。然后就可以对 \([l,mid-1]\) 和 \([mid,r]\) 进行递归了。

注意到在分治的过程中同一层的区间可以一起做,同时由于同一层的相邻区间会相互影响,所以需要对于奇数和偶数区间分别做。询问次数大概为 \(92\) 次,过不了。

又因为做完 \(mid-l\) 的询问后一定可以确定递归区间分别有那些数了,所以 \(mid-l-1\) 的询问不需要奇偶分组,放到一起做即可。

加上上面那个优化后询问次数就变为 \(80\) 了,可以通过。

Code

#include <bits/stdc++.h>

#ifdef ORZXKR
#include "grader.cc"
#else
#include "god.h"
#endif

const int kMaxN = 1.6e4 + 5;

int n;
int d[kMaxN], dep[kMaxN], cnt[kMaxN], p[kMaxN];
bool res[kMaxN], vis[kMaxN];
std::pair<int, int> seg[kMaxN * 4];
std::vector<int> id[kMaxN], ss[30], sv[kMaxN * 4][2];

namespace REAL {
void solve(int d, int x, int l, int r) {
  seg[x] = {l, r};
  if (r - l <= 1) return;
  int mid = (l + r + 1) >> 1;
  ss[d].emplace_back(x);
  solve(d + 1, x << 1, l, mid - 1), solve(d + 1, x << 1 | 1, mid, r);
}

void getdep() {
  solve(1, 1, 0, n - 1);
  sv[1][0] = {1};
  for (int i = 2; i <= n; ++i) sv[1][1].emplace_back(i);
  for (int c = 1; c <= 20; ++c) {
    if (!ss[c].size()) continue;
    static int d[3][kMaxN];
    static bool res[3][kMaxN];
    std::fill_n(d[0] + 1, n, -1);
    std::fill_n(d[1] + 1, n, -1);
    std::fill_n(d[2] + 1, n, -1);
    for (int r = 0; r < 2; ++r) {
      for (int i = r; i < (int)ss[c].size(); i += 2) {
        int x = ss[c][i];
        std::pair<int, int> p = seg[x];
        int mid = (p.first + p.second + 1) / 2;
        for (auto j : sv[x][0]) d[r][j] = mid - p.first;
      }
    }
    query(d[0], res[0]);
    if (c != 1) query(d[1], res[1]);
    for (auto x : ss[c]) {
      std::pair<int, int> p = seg[x];
      int mid = (p.first + p.second + 1) / 2;
      for (auto j : sv[x][0]) d[2][j] = mid - p.first - 1;
    }
    query(d[2], res[2]);
    for (int i = 0; i < (int)ss[c].size(); ++i) {
      int x = ss[c][i];
      sv[x << 1][0] = sv[x][0];
      for (auto j : sv[x][1]) {
        if (!res[i & 1][j]) sv[x << 1 | 1][1].emplace_back(j);
        else if (res[2][j]) sv[x << 1][1].emplace_back(j);
        else sv[x << 1 | 1][0].emplace_back(j);
      }
    }
  }
  for (int i = 1; i <= 4 * n; ++i) {
    if (seg[i].second - seg[i].first <= 1 && (sv[i][0].size() || sv[i][1].size())) {
      for (auto x : sv[i][0]) dep[x] = seg[i].first; 
      for (auto x : sv[i][1]) dep[x] = seg[i].second;
    }
  }

  for (int i = 1; i <= n; ++i) {
    id[dep[i]].emplace_back(i);
  }
}

void getedge() {
  for (int r = 0; r < 3; ++r) {
    for (int b = 0; (1 << b) <= n; ++b) {
      std::fill_n(d + 1, n, -1);
      std::fill_n(res + 1, n, 0);
      for (int i = r; i <= n; i += 3) {
        for (auto x : id[i]) {
          if ((x >> b & 1)) d[x] = 1;
        }
      }
      if (*std::max_element(d + 1, d + 1 + n) == 1) query(d, res);
      for (int i = 1; i <= n; ++i)
        if (res[i] && dep[i] % 3 == (r + 1) % 3)
          p[i] |= (1 << b);
    }
  }
  for (int i = 2; i <= n; ++i) std::cout << p[i] << ' ' << i << '\n';
}

void solve() {
  std::fill_n(p + 1, n, 0);
  std::fill_n(vis + 1, n, 0);
  std::fill_n(cnt, n + 1, 0);
  for (int i = 0; i <= n; ++i) id[i].clear();
  for (int i = 1; i <= 20; ++i) ss[i].clear();
  for (int i = 1; i <= n * 4; ++i) sv[i][0].clear(), sv[i][1].clear();
  getdep(), getedge();
}
} // namespace REAL

namespace SAMPLE {
int fa[kMaxN];

int find(int x) {
  return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void solve() {
  if (n == 5) return void(std::cout << "2 1\n5 3\n2 4\n2 3\n");
  std::fill_n(d + 1, n, -1);
  d[1] = 1;
  query(d, res);
  int cnt = 0;
  for (int i = 1; i <= n; ++i) cnt += res[i];
  if (cnt == 2) {
    for (int i = 1; i < n; ++i) std::cout << i << ' ' << i + 1 << '\n';
  } else {
    std::iota(fa + 1, fa + 1 + n, 1);
    for (int i = 1; i < n; ++i) {
      int x = rand() % n + 1, y = rand() % n + 1;
      while (find(x) == find(y)) {
        x = rand() % n + 1, y = rand() % n + 1;
      }
      std::cout << x << ' ' << y << '\n';
      fa[find(x)] = find(y);
    }
  }
}
} // namespace SAMPLE

void wxy_god(int n, int subtask) {
  ::n = n;
  if (subtask == 0) return SAMPLE::solve();
  else REAL::solve();
}

标签:int,题解,T4,mid,kMaxN,leq,20240924,深度,询问
From: https://www.cnblogs.com/Scarab/p/18430258

相关文章

  • T4式子
    我们要求的是:\[n!\sum_{k=1}^{n}[x^n]e_{k}^{m}{(x)}\]其中\([x^n]\)表示\(x^n\)的系数。首先对\(e^m_{k}(x)\)求导,这里用到了复合函数的求导,(这里简单写了,不是很严谨)即:对于两个函数\(f(x)、g(x)\),令\(h(x)=f[g(x)]\),则\(h'(x)=f'[g(x)]g'(x)\)这里我们可以把......
  • P4780 Phi的反函数 题解
    因为\(\varphi(x)\)的值只与\(x\)的不同质因子有关,又因为\(2^{31}\)之内的数的质因子个数不超过\(10\),所以容易枚举\(10\)个位置上填入的质因子打出朴素的暴力,简单剪枝后得到\(20\)分。注意需要先判掉\(x=n+1\)的情况。考虑优化:因为\(\varphi\)的值只与质因......
  • Codeforces Round 959 (Div. 1 + Div. 2) C. Hungry Games题解
    CodeforcesRound959(Div.1+Div.2)C.HungryGames题解题目链接大致题意:给定一个长度为n的数组,并且给出一对l,r表示一个区间,如果∑i......
  • 20240924 练习记录
    3个DP,还想了几道题,但不会。*P3349[ZJOI2016]小星星考虑树上的点最终会对应在图上的哪个点,设\(f_{x,i}\)表示树上的点\(x\)对应图上点\(i\)时的方案数,当\(x\)对应\(i\)后,在树上\(x\)的所有子节点也必须像在树上一样,在图上和\(i\)之间有连边,有了这条限制,可以写......
  • 力扣题解2207
    大家好,欢迎来到无限大的频道。今日继续给大家带来力扣题解。题目描述(中等)​:字符串中最多数目的子序列给你一个下标从 0 开始的字符串 text 和另一个下标从 0 开始且长度为 2 的字符串 pattern ,两者都只包含小写英文字母。你可以在 text 中任意位置插入 一个......
  • 【洛谷】P2261 [CQOI2007] 余数求和 的题解
    【洛谷】P2261[CQOI2007]余数求和的题解洛谷传送门题解这还是蓝题,这还是省选qaq题目看着很简单,但是真的很考验思路,思路对了,代码不到555分钟写完。刚开始做的时......
  • 【Day20240924】05git 两人协作 冲突
    git两人协作冲突命令行解决两个人修改同一文件时的冲突可视化解决两个人修改同一文件时的冲突参考命令行解决两个人修改同一文件时的冲突假设kerwin.js是项目的路由文件。tiechui文件夹是组员铁锤的工作目录;test2008文件夹是组长的工作目录。此时,两人都想要......
  • 题解:SP1741 TETRIS3D - Tetris 3D
    题意维护一个\(D\timesS\)的平面,每个点有一个高度。要求支持一个操作:查询一个矩形区域的最大值,并将该区域更新为最大值加上给定的数。分析发现\(D,S\leq10^3\),考虑使用二维线段树维护。二维线段树,顾名思义,就是在普通线段树的每一个节点上维护一棵线段树。在本题中,外层节......
  • 题解:P10950 太鼓达人
    分析显然答案包含长度为\(K\)的所有\(01\)串,每个串和前一个的重叠长度为\(K-1\),所以每个串对长度的贡献为\(1\)。因此该串的长度为所有\(01\)串的个数,即\(2^K\)。考虑第二个如何解决。发现每个位置的状态只有\(0\)和\(1\),考虑爆搜。显然直接搜的复杂度为\(O(2^......
  • 题解:P5184 [COCI2009-2010#2] PASIJANS
    分析考虑贪心,每次尽量选最小的字符。显然是每次选字典序最小的弹栈。我们要比较的是每个栈的字典序,但是朴素比较是\(O(L)\)的,考虑将它优化到\(O(1)\)。这个时候我们可以先离散化然后套路地将所有串拼一起跑SA。记得在每个串之间加分割符。这样每次比较字典序就变成了\(......