首页 > 其他分享 >P10218 [省选联考 2024] 魔法手杖 题解

P10218 [省选联考 2024] 魔法手杖 题解

时间:2024-03-17 17:45:09浏览次数:31  
标签:cur 省选 题解 nowans trie ans 联考 minv pw

Description

给定 \(a_1,a_2,\dots,a_n\) 以及 \(b_1,b_2,\dots,b_n\),满足 \(a_i \in [0,2^k-1]\) 以及 \(b_i\geq 0\),你需要给出 \(S \subseteq \{1,2,\dots,n\}\) 以及 \(x \in [0,2^k-1]\) 满足以下条件:

  • \(\sum \limits_{i\in S} b_i\leq m\);
  • 满足以上条件的前提下,最大化 \(val(S,x)=\min(\min \limits_{i \in S}(a_i+x),\min \limits_{i \in U \backslash S}(a_i \oplus x))\) 的值。

你只需要给出最大的 \(val(S,x)\) 的值即可。


设 \(\sum n\) 表示单组测试点各组数据 \(n\) 的和。对于所有测试数据,

  • \(T \geq 1\);
  • \(1 \leq n \leq 10^5\),\(1 \leq \sum n \leq 5\times 10^5\);
  • \(0 \leq m \leq 10^9\);
  • \(0 \leq k \leq 120\);
  • \(\forall 1 \leq i \leq n, 0 \leq a_i<2^k\);
  • \(\forall 1 \leq i \leq n, 0 \leq b_i \leq 10^9\)。

Solution

考虑建出 \(a\) 数组的 trie 树然后按照从高位到低位确定答案。

设当前走到第 \(k\) 位,trie 树节点权值为 \(val\),目前确定了 \(x\) 和 \(ans\) 的这些位,\(nowm\) 表示剩下的权值,\(minv\) 表示目前已经确定放到 \(S\) 中的数 \(a\) 的最小值。容易发现 \(x\oplus val=ans\)。

首先需要判断 \(ans\) 的第 \(k\) 位能否为 \(1\)。

不妨设 \(x\) 这位为 \(0\),对于 \(1\) 的情况同理。那么要让 \(ans\) 这位为 \(1\),就必须要把 \(val\) 子树内这位为 \(0\) 的数加到 \(S\) 中去,因为单靠异或是不能满足要求的。

那么如果 \(\min\{minv,minb_{ch_{val,0}}\}+2^k-1\geq ans+2^k\),就能满足 \(+\) 的部分大于等于 \(ans+2^k\),因为这里让后面 \(x\) 的位尽可能大一定是最优的。于是往这个儿子递归求答案即可。


然后是对于 \(ans\) 的第 \(k\) 位不能为 \(1\) 的情况。

还是设 \(x\) 这位为 \(0\)。那么 \(val\) 子树内为 \(1\) 的显然异或出来 \(\geq ans+2^k\),就不用管了,直接走到 \(ch_{val,0}\) 递归求解即可。

如果当前的 \(val\) 只有一个儿子需要特判。

容易发现对于 trie 树上的每个点只会遍历一次,所以时间复杂度是 \(O(nk)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

namespace FASTIO {
char ibuf[1 << 21], *p1 = ibuf, *p2 = ibuf;
char getc() {
  return p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
template<class T> bool read(T &x) {
  x = 0; int f = 0; char ch = getc();
  while (ch < '0' || ch > '9') f |= ch == '-', ch = getc();
  while (ch >= '0' && ch <= '9') x = (x * 10) + (ch ^ 48), ch = getc();
  x = (f ? -x : x); return 1;
}
template<typename A, typename ...B> bool read(A &x, B &...y) { return read(x) && read(y...); }
 
char obuf[1 << 21], *o1 = obuf, *o2 = obuf + (1 << 21) - 1;
void flush() { fwrite(obuf, 1, o1 - obuf, stdout), o1 = obuf; }
void putc(char x) { *o1++ = x; if (o1 == o2) flush(); }
template<class T> void write(T x) {
  if (!x) putc('0');
  if (x < 0) x = -x, putc('-');
  char c[40]; int tot = 0;
  while (x) c[++tot] = x % 10, x /= 10;
  for (int i = tot; i; --i) putc(c[i] + '0');
}
void write(char x) { putc(x); }
template<typename A, typename ...B> void write(A x, B ...y) { write(x), write(y...); }
struct Flusher {
  ~Flusher() { flush(); }
} flusher;
} // namespace FASTIO
using FASTIO::read; using FASTIO::putc; using FASTIO::write;

using i128 = __int128_t;

const int kMaxN = 1e5 + 5, kMaxK = 124, kMaxT = kMaxN * kMaxK;

int sid, n, m, k, tot;
int b[kMaxN], trie[kMaxT][2];
int64_t sumb[kMaxT];
i128 ans, a[kMaxN], mina[kMaxT];

void ins(i128 a, int b) {
  int cur = 0;
  for (int i = k - 1; ~i; --i) {
    int c = (a >> i & 1);
    if (!trie[cur][c]) trie[cur][c] = ++tot, mina[tot] = a;
    cur = trie[cur][c];
    mina[cur] = std::min(mina[cur], a), sumb[cur] += b;
  }
}

void solve(int cur, i128 nowval, i128 x, i128 nowans, i128 minv, int m, int k) {
  assert((x ^ nowans) == nowval);
  assert(m >= 0);
  if (!~k) {
    ans = std::max(ans, nowans);
    return;
  }
  assert(trie[cur][0] || trie[cur][1]);
  i128 pw = (i128)1 << k;
  if (!trie[cur][0]) {
    // x 这位为 0
    bool fl = 0;
    if (minv + x + pw - 1 >= nowans + pw) {
      fl = 1;
      solve(trie[cur][1], nowval + pw, x, nowans + pw, minv, m, k - 1);
    }
    // x 这位为 1
    if (std::min(minv, mina[trie[cur][1]]) + x + pw + pw - 1 >= nowans + pw && m >= sumb[trie[cur][1]]) {
      fl = 1;
      ans = std::max(ans, std::min(std::min(minv, mina[trie[cur][1]]) + x + pw + pw - 1, nowans + pw + pw - 1));
    }

    if (!fl) {
      ans = std::max(ans, std::min(nowans + pw - 1, minv + x + pw - 1));
      solve(trie[cur][1], nowval + pw, x + pw, nowans, minv, m, k - 1);
    }
  } else if (!trie[cur][1]) {
    bool fl = 0;
    // x 这位为 1
    if (minv + x + pw + pw - 1 >= nowans + pw) {
      fl = 1;
      solve(trie[cur][0], nowval, x + pw, nowans + pw, minv, m, k - 1);
    }
    // x 这位为 0
    if (std::min(minv, mina[trie[cur][0]]) + x + pw - 1 >= nowans + pw && m >= sumb[trie[cur][0]]) {
      fl = 1;
      ans = std::max(ans, std::min(std::min(minv, mina[trie[cur][1]]) + x + pw - 1, nowans + pw + pw - 1));
    }
    if (!fl) {
      ans = std::max(ans, std::min(nowans + pw - 1, minv + x + pw + pw - 1));
      solve(trie[cur][0], nowval, x, nowans, minv, m, k - 1);
    }
  } else {
    // ans 这位为 1
    assert(trie[cur][0] && trie[cur][1]);
    bool fl = 0;
    if (std::min(minv, mina[trie[cur][0]]) + x + pw - 1 >= nowans + pw && m >= sumb[trie[cur][0]]) {
      fl = 1;
      solve(trie[cur][1], nowval + pw, x, nowans + pw, std::min(minv, mina[trie[cur][0]]), m - sumb[trie[cur][0]], k - 1);
    }
    if (std::min(minv, mina[trie[cur][1]]) + x + pw + pw - 1 >= nowans + pw && m >= sumb[trie[cur][1]]) {
      fl = 1;
      solve(trie[cur][0], nowval, x + pw, nowans + pw, std::min(minv, mina[trie[cur][1]]), m - sumb[trie[cur][1]], k - 1);
    }
    if (!fl) {
      solve(trie[cur][0], nowval, x, nowans, minv, m, k - 1);
      solve(trie[cur][1], nowval + pw, x + pw, nowans, minv, m, k - 1);
    }
  }
}

void dickdreamer() {
  read(n, m, k);
  for (int i = 0; i <= tot; ++i) {
    trie[i][0] = trie[i][1] = sumb[i] = mina[i] = 0;
  }
  tot = 0;
  for (int i = 1; i <= n; ++i) read(a[i]);
  int64_t tsb = 0;
  for (int i = 1; i <= n; ++i) {
    read(b[i]);
    ins(a[i], b[i]);
    tsb += b[i];
  }
  ans = 0, solve(0, 0, 0, 0, ((i128)1 << k) - 1, m, k - 1);
  if (tsb <= m) {
    ans = std::max(ans, *std::min_element(a + 1, a + 1 + n) + ((i128)1 << k) - 1);
  }
  write(ans, '\n');
}

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

标签:cur,省选,题解,nowans,trie,ans,联考,minv,pw
From: https://www.cnblogs.com/Scarab/p/18078828

相关文章

  • P2746 [USACO5.3] 校园网Network of Schools 题解
    题目链接:校园网NetworkofSchools这个题得翻译下题目意思才知道在干嘛,题目一开始表明了这个是一个有向图,因为边是单向的。其次关于第一个问题:基于一个事实,如果有\(x\rightarrowy\rightarrowz\),那么只需要\(x\)接受协议,它所在的\(scc\)强连通分量上的点一定都能不需要......
  • AT_abc345_c [ABC345C] One Time Swap 题解
    题目传送门解法对于\(S_{i}\),设\(num_{S_{i}}\)表示\(S_{i+1\simn}\)中\(S_{i}\)出现的次数,则\(S_{i}\)对答案产生的贡献为\(n-i-num_{S_{i}}\)。注意原串在存在两个相同的元素的时候,也要统计在内。代码#include<bits/stdc++.h>usingnamespacestd;#definell......
  • P2163 [SHOI2007] 园丁的烦恼 题解
    题目链接:园丁的烦恼挺经典的题目,转化成二维数点去做这玩意和常规的偏序计数问题有区别:转化为求\(a\lex\leb\\&\&\c\ley\led\)的数量,这种就别想着拆来拆去了,这种权值类带偏序计数类问题,是经典的可差性问题,我们计:\(ans(x,l,r)\)表示\(t\lex,l\ley\ler\)的数......
  • P3302 [SDOI2013] 森林 题解
    题目链接:森林有意思的树上可持久化线段树变形题,建议先看这个:P2633Countonatree题解对于本题而言,我们重新阐述树上可持久化线段树的核心思想,对于点路径/边路径上的第\(k\)大问题,我们使用树上前缀和问题的思想,将其转化为可差性问题:一条路径上的权值线段树可以拆分为几棵权......
  • 贪心算法题解
    前言大家好,我是jiantaoyab,这篇文章将给大家介绍贪心算法和贪心算法题目的练习和解析,贪心算法的本质就是每一个阶段都是局部最优,从而实现全局最优。我们在做题的同时,不仅要把题目做出来,还要有严格的证明。柠檬水找零在柠檬水摊上,每一杯柠檬水的售价为5美元。顾客排队......
  • codeforce Round 934 div2 个人题解(A~C)
    A.DestroyingBridges时间限制:1秒内存限制:256兆输入:标准输入输出:标准输出有$n$个岛屿,编号为$1,2,…,n$。最初,每对岛屿都由一座桥连接。因此,一共有$\frac{n(n-1)}{2}$座桥。Everule住在岛屿$1$上,喜欢利用桥梁访问其他岛屿。Dominater有能力摧毁最多$k$座......
  • AtCoder-abc345_f题解
    题意简述给定一个无向图。你要在其中选出一些边,使得选出的边所构成的图中,度数为奇数的点有\(K\)个。如果可以,输出选了哪些边,否则输出-1。思路首先在选一条边时,边两端点度数的奇偶性一定都会改变,即要么都变为奇数,要么两个点的奇偶性交换过来,要么都变为偶数。这三种情况时满足......
  • P2633 Count on a tree 题解
    题目链接:Countonatree大概可以认为是树上主席树的板子我在之前的某些题解提到了,主席树一般来说有两个基本功能:可持久化功能,可以选择回退或者新增版本。对于可差性问题,可以有更好的转化为前缀和做法,常见的问题为权值类型问题。在树上的路径第\(k\)大,显然如果我们能......
  • 【洛谷 P8661】[蓝桥杯 2018 省 B] 日志统计 题解(滑动窗口+优先队列+双端队列+集合)
    [蓝桥杯2018省B]日志统计题目描述小明维护着一个程序员论坛。现在他收集了一份“点赞”日志,日志共有NNN行。其中每一行的格式是tsid,表示在......
  • 【洛谷 P8602】[蓝桥杯 2013 省 A] 大臣的旅费 题解(图论+深度优先搜索+树的直径+链式
    [蓝桥杯2013省A]大臣的旅费题目描述很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同......