首页 > 其他分享 >Solution - Luogu P11472 命运黄之瓜

Solution - Luogu P11472 命运黄之瓜

时间:2024-12-30 21:42:58浏览次数:1  
标签:ge leftarrow int Luogu 黄之瓜 Solution 异或 oplus ll

因为 \((a_i, b_i)\) 虽然是对的形式,但是异或是同时的。
于是可以考虑把两元先缩为一元,只需要让 \(a_i, b_i\) 互不干扰即可。
那就可以把 \((a_i, b_i)\) 当作数 \(c_i = a_i\times 2^{31} + b_i\)。
那么最后 \(c_i\) 异或出来的结果 \(c\),就可以还原出 \(a = \lfloor\frac{c}{2^{31}}\rfloor, b = c\bmod 2^{31}\)。

因为 \(n\) 比较大,肯定还是想 \(n\) 尽量小。
结合操作是异或,可以考虑对 \(c\) 建出线性基,那么这样新 \(n\) 就被缩减到了 \(\mathcal{O}(\log V)\)。

接下来考虑来求解,因为对于 \(\min(a, b)\) 是一个双向限制,一个想法是去掉一个方向上的限制。
那么就可以考虑去二分 \(k = \min(a, b)\),那么只保留下 \(b\ge k\) 这个限制(为什么是选择 \(b\) 而不是 \(a\),将会在后面说到),对于 \(a\) 直接考虑最大化并在最后判断 \(a, k\) 的大小关系就行了。

那么接下来考虑如何满足 \(b\ge k\) 的同时最大化 \(a\)。
首先如果最大化 \(b\) 都无法使得 \(b\ge k\),那肯定不合法。
注意到建完线性基其实还有一个很好的性质是,对于每个最高位,至多有一个元素的最高位为该位。
再结合上对于线性基求异或最大值时常用的一个贪心的方法是直接从高位到低位,\(\operatorname{ans}\leftarrow \max\{\operatorname{ans}, \operatorname{ans}\oplus a_i\}\),这实际上也是上述的性质保证了正确性。
那么这个时候能发现,因为以 \(c_i = a_i\times 2^{31} + b_i\) 作为元素,最后的线性基对于 \(a_i\) 的每个位最多只有一个元素最高位为此位,否则就是 \(a_i = 0\)。

那么能发现此时对于 \(a\) 来说依然能用上述所提到的贪心 \(a\leftarrow \operatorname{max}\{a, a\oplus a_i\}\),这也是选取最大化 \(a\) 而不是 \(b\) 的原因,因为 \(b_i\) 不满足此性质。

那么就可以对于 \(\mathcal{O}(\log V)\) 组 \((a_i, b_i)\),按照 \(a_i\) 从大到小排序,然后依次决定是否要让 \(a\leftarrow a\oplus a_i\)。
那么就可以维护当前的 \(a, b\),接下来考虑 \((a_i, b_i)\) 这个对是否要异或上,就可以考虑分讨:

  • 如果不异或保留 \(b\),在 \([i + 1, n]\) 无法使得 \(b\) 能被异或出 \(\ge k\)。
    那么就必须要让 \(b\leftarrow b\oplus b_i\)。
  • 否则此时已经可以选择不异或保留 \(b\) 了。
    那么如果 \(a\oplus a_i > a\) 且 \(b\oplus b_i\) 能在 \([i + 1, n]\) 中被异或使得 \(\ge k\),就会贪心的选择异或,即 \(a\leftarrow a\oplus a_i, b\leftarrow b\oplus b_i\)。
    否则就会选择保留,即 \(a, b\) 不变。

至于如何判断 \([i + 1, n]\) 能否通过异或使得 \(x\) 异或后 \(\ge k\)。
依然可以考虑最大化异或值,然后判断是否 \(\ge k\)。
那么就可以建一个后缀的线性基,每次借用 \([i + 1, n]\) 的信息插入 \(b_i\),就得到了 \([i, n]\) 的信息。

时间复杂度 \(\mathcal{O}(n\log V + \log^3 V)\)。

#include<bits/stdc++.h>
using ll = long long;
constexpr int maxn = 2e5 + 10, maxV = 65;
ll a[maxn], b[maxn], c[maxn];
ll f[maxV][maxV];
inline void solve() {
   int n;
   scanf("%d", &n);
   for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
   for (int i = 1; i <= n; i++) scanf("%lld", &b[i]);
   for (int i = 1; i <= n; i++) c[i] = (a[i] << 31) | b[i];
   for (int w = 61, p = 1; ~ w; w--) {
      for (int i = p; i <= n; i++) {
         if (c[i] >> w & 1ll) {
            std::swap(c[i], c[p]);
         }
      }
      if (~ c[p] >> w & 1ll) continue;
      for (int i = p + 1; i <= n; i++) {
         if (c[i] >> w & 1ll) {
            c[i] ^= c[p];
         }
      }
      p++;
   }
   c[n + 1] = 0;
   for (n = 1; c[n]; n++);
   n--;
   assert(n <= 62);
   for (int i = 1; i <= n; i++) {
      a[i] = c[i] >> 31, b[i] = c[i] ^ (a[i] << 31);
   }
   memset(f[n + 1], 0, sizeof(f[n + 1]));
   for (int i = n; i; i--) {
      memcpy(f[i], f[i + 1], sizeof(f[i]));
      ll x = b[i];
      for (int w = 30; ~ w; w--) {
         if (~ x >> w & 1ll) continue;
         if (! f[i][w]) f[i][w] = x;
         x ^= f[i][w];
      }
   }
   auto query = [&](ll *F, ll x) {
      for (int w = 30; ~ w; w--) {
         x = std::max(x, x ^ F[w]);
      }
      return x;
   };
   auto check = [&](ll val) {
      if (query(f[1], 0ll) < val) return false;
      ll x = 0, y = 0;
      for (int i = 1; i <= n; i++) {
         if (query(f[i + 1], y) < val) {
            x ^= a[i], y ^= b[i];
         } else if (query(f[i + 1], y ^ b[i]) >= val && (x ^ a[i]) > x) {
            x ^= a[i], y ^= b[i];
         }
      }
      assert(y >= val);
      return x >= val;
   };
   ll l = 0, r = INT_MAX, ans = 0;
   while (l <= r) {
      ll mid = l + r >> 1;
      if (check(mid)) ans = mid, l = mid + 1;
      else r = mid - 1;
   }
   printf("%lld\n", ans);
}
int main() {
   for (int T, _ = scanf("%d", &T); T--; ) {
      solve();
   }
   return 0;
}

标签:ge,leftarrow,int,Luogu,黄之瓜,Solution,异或,oplus,ll
From: https://www.cnblogs.com/rizynvu/p/18642417

相关文章

  • [luoguP4556] [Vani有约会] 雨天的尾巴
    题意给定\(n\)个点的无根树,进行\(m\)次操作,每次使\(x\toy\)路径上的每个点的可重集合内都插入一个\(z\),求每个点的可重集合内最多的数是多少,数量相同输出最小的。sol路径操作、离线,因此可以想到树上差分。开一个数组,记录每个点的可重集合内每个数的个数,然后做树上差分......
  • [luoguP10218/省选联考 2024] 魔法手杖
    题意给定\(a_1,a_2,\dots,a_n\)以及\(b_1,b_2,\dots,b_n\),满足\(a_i\in[0,2^k-1]\)以及\(b_i\geq0\),你需要给出\(S\subseteq\{1,2,\dots,n\}\)以及\(x\in[0,2^k-1]\)满足以下条件:\(\sum\limits_{i\inS}b_i\leqm\);满足以上条件的前提下,最大化\(val(S,x)......
  • [PA2019] Desant Solution
    [PA2019]DesantSolution原题链接。题目大意:给定一个长为\(n(n\le40)\)的排列,对于每个\(i\)求出长度为\(i\)的子序列逆序对最少有多少,并且求出有多少个长度为\(i\)的子序列逆序对最少。解题思路:首先有一个暴力的做法,设\(f_{i,S}\)表示考虑完前\(i\)个数,选择了集......
  • Luogu EI 的第六分块 // KTT 学习记录
    P5693EI的第六分块题目描述给定一个整数序列,支持区间加正整数以及查询区间最大子段和。思路使用线段树记录四个信息来维护答案:\(sum_i\):区间和;\(lmax_i\):最大前缀和;\(rmax_i\):最大后缀和;\(mx_i\):最大子段和。信息合并时分类讨论:\(lmax=\max(lmax_{ls},sum_{ls}+l......
  • [ARC127D] Sum of Min of Xor Solution
    题意给定两个长度为\(n\)的数组\(a,b\),求\[\sum_{i=1}^n\sum_{j=i+1}^n\min\{a_i\oplusa_j,b_i\oplusb_j\}\]其中\(\oplus\)表示按位异或。分析简单二进制分治题(看代码可能更好理解)。先将有序对转成无序对,最后答案为结果的一半。考虑去除\(\min\),于是拆位计算贡献......
  • [ABC283Ex] Popcount Sum Solution
    有点意思的黑题。\[\begin{aligned}{}&\sum_{i\bmodm\=\r}^{n}\rm{popcount}(i)\\&=\sum_{i=0}^{\lfloor\frac{n-r}{m}\rfloor}\rm{popcount}(im+r)\\&=\sum_{i=0}^{\lfloor\frac{n-r}{m}\rfloor}\sum_{j=0}^{\log(im+r)}......
  • Solution - Luogu P11405 [RMI 2020] 秘鲁 / Peru
    考虑到区间可能会有交,这个时候肯定会贪心的让这部分的权值为偏大的一部分。于是考虑把条件转化为由若干个长度\(\lek\)的不交区间覆盖。那么如果对应的区间是\([l,r]\),那么贪心的,这个区间选出来的权值就会是\(\max\limits_{i=l}^rs_i\)。那么就可以设出dp。定义\(f......
  • Solution - Luogu P11402 [Code+#8 初赛] 图
    首先通过手玩,发现对于小的\(n\)都有\(m_{\max}\len\),于是直接猜测这个结论并尝试证明。首先对于\(n\le4\)的情况,首先可以直接通过手玩知道\(m_{\max}\len\)。对于\(n>4\)的情况,考虑\(n\)从小到大证明。若\(m>n\),则\(\sum\limits_{i=1}^n\operatorname{de......
  • Solution - Luogu P11394 [JOI Open 2019] ウイルス実験
    首先可以根据字符串\(D\),\(\mathcal{O}(2^c|D|)\)(\(c\)为方向数\(4\))求出上下左右分别是否被感染时对应的最长连续段长度,用于后面的check。考虑到答案要求的最小值,于是可以考虑思考什么样的点不会作为最后的最小值的起始点。考虑到如果最先感染了点\(u\),且最终感染了点\(v......
  • AtCoder Beginner Contest 385 Solution
    A-Equally(abc385A)题目大意给定三个数,问能不能分成两个以上的组,使其和相同。解题思路两个以上的组要么是两组要么是三组,三组就是三个数都相等,两组就是两个小的加起来等于大的。代码voidsolve(){inta[10];cin>>a[0]>>a[1]>>a[2];sort(a,a+3)......