首页 > 其他分享 >gym103102H AND = OR 题解

gym103102H AND = OR 题解

时间:2024-11-12 20:50:23浏览次数:1  
标签:GCC int 题解 popcount pragma text gym103102H optimize

非常巧妙的一个题。我们首先考虑单组询问该怎么做。

首先需要注意到一个结论,即设答案为 \(x\),那么对于 \(\forall y < x\),\(y\) 都应该放在与组;同样的,对于 \(\forall y > x\),\(y\) 都应该放在与组。

进一步的,我们观察在 \(\text{popcount}\) 上也有同样的性质,即对于 \(\forall y, \text{popcount(y)} < \text{popcount(x)}\),\(y\) 应该被放到或组,反之同理。

而对于 \(\text{popcount(y) = \text{popcount(x)}}\) 的 \(y\),有结论:所有的 \(y\) 相等。

证明非常的巧妙,设或集合为 \(A\),与集合为 \(B\),\(\text{popcount(x)} = k\)。

当只有一个数时显然成立,现在考虑大于一个数的情况。

从这些数中拿出两个,分别设为 \(a, b\),并分别放入 \(A, B\) 中,设 \(A, B\) 内的数经运算后的值分别为 \(v1, v2\)。

显然有 \(\text{popcount}(v1) \ge k, \text{popcount}(v2) \le k\),又因为两个集合的答案相等,所以 \(\text{popcount}(v1) = \text{popcount}(v2) = k\)。

考虑按位与或的本质,是把二进制下的位置分别取并和交,因为两者都恰好有 \(k\) 位,所以 \(v1 = a, v2 = b\),那么进一步就有 \(a = b\)。

大于 \(2\) 个数字都可以用类似的证明。

考虑答案怎么维护,考虑枚举答案的 \(\text{popcount} = k\),依次 check 每个区间是否合法,维护 \(\text{popcount} < k\) 的 \(A\) 集合值和 \(\text{popcount} > k\) 的 \(B\) 集合值,可以用线段树和前缀和在 \(\log^2\) 内完成。

细节上对于 \(\text{popcount} = k\) 的数,要分别对数量为 \(1, 2, \ge 2\) 分别讨论。

// ubsan: undefined
// accoders
// 如果命运对你缄默, 那就活给他看。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
typedef long long LL; 
// #define int LL
const int maxn = 100010;
const int INF = (1 << 30) - 1;
int n, q, w[maxn], pc[maxn];
namespace sgt {
  struct Node { int o, a, c, v; };
  struct sgt {
    Node t[maxn << 2];
    inline Node mg(Node ls, Node rs) {
      return {ls.o | rs.o, ls.a & rs.a, ls.c + rs.c, 
        (ls.v == -1 || rs.v == -1) ? max(ls.v, rs.v) : ((ls.v ^ rs.v) ? INF + 1 : rs.v)
      }; 
    }
    inline void build(int u, int l, int r, int c) {
      if(l == r) {
        if(pc[r] == c) t[u] = {w[r], w[r], 1, w[r]};
        else t[u] = {0, INF, 0, -1};
        return ;
      }
      int mid = l + r >> 1;
      build(u << 1, l, mid, c);
      build(u << 1 | 1, mid + 1, r, c);
      t[u] = mg(t[u << 1], t[u << 1 | 1]);
    }
    inline Node Q(int u, int l, int r, int ql, int qr) {
      if(ql <= l && r <= qr) return t[u];
      int mid = l + r >> 1;
      if(qr <= mid) return Q(u << 1, l, mid, ql, qr);
      if(ql > mid) return Q(u << 1 | 1, mid + 1, r, ql, qr);
      return mg(Q(u << 1, l, mid, ql, qr), Q(u << 1 | 1, mid + 1, r, ql, qr));
    }
  } t[31];
} using namespace sgt; 
int a[31], o[31];
inline bool check(int l, int r) {
  for(int i = 0; i < 31; ++ i) {
    Node e = t[i].Q(1, 1, n, l, r);
    a[i] = e.a, o[i] = e.o;
  }
  for(int i = 1; i < 31; ++ i) o[i] |= o[i - 1];
  for(int i = 29; ~i; -- i) a[i] &= a[i + 1];
  int co = 0, ca = r - l + 1;
  for(int k = 0; k < 31; ++ k) {
    Node e = t[k].Q(1, 1, n, l, r);
    ca -= e.c;
    int vo = (!k) ? 0 : o[k - 1], va = (k == 30 ? INF : a[k + 1]);
    if(e.v ^ (INF + 1)) {
      int cnt = e.c;
      if((cnt == 0) && (ca && co && (vo == va))) return 1;
      if((cnt == 1) && ((((vo | e.v) == va) && ca) || ((vo == (e.v & va)) && co))) return 1;
      if((cnt >= 2) && ((vo | e.v) == (va & e.v))) return 1; 
    }
    co += e.c;
  }
  return 0;
}
signed main() {
//   freopen("peace.in", "r", stdin);
//   freopen("peace.out", "w", stdout);
  ios :: sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  cin >> n >> q;
  for(int i = 1; i <= n; ++ i) {
    cin >> w[i];
    pc[i] = __builtin_popcount(w[i]);
  }
  for(int i = 0; i < 31; ++ i) t[i].build(1, 1, n, i);
  for(int l, r, i = 1; q --; ) {
    cin >> l >> r;
    cout << (check(l, r) ? "YES" : "NO") << '\n';
  }
  return 0;
}

标签:GCC,int,题解,popcount,pragma,text,gym103102H,optimize
From: https://www.cnblogs.com/Rainsheep/p/18542616

相关文章

  • 好题记录 [集训队互测 2023] 优惠购物 题解
    首先发现这个过程的限制比较多,那么考虑重新描述这个过程。令\(x_i\)表示在第\(i\)个物品上使用了\(x_i\)张券,那么一组\(x_{1\simn}\)就描述了一个方案。方便起见,令\(s_i\)为前i个物品买完后剩了几张券,那么有:\(s_0=m\)\(s_i=s_{i-1}+\lfloor\frac{a_i-......
  • 【题解】洛谷P7287: 「EZEC-5」魔法
    P7287「EZEC-5」魔法感觉好题有思维,但是我没认真读题,看到样例就我以为了,他让任意一个区间满足大于\(S\)即可不是全部。我们手搓一下样例就可以发现,对于加法我们不加白不加的话肯定全部的数都加上,乘法肯定要等到加完后才开始,这些都是贪心思路。然后就是开始搭配操作了,我遇到......
  • 题解:「2017 山东一轮集训 Day7」逆序对
    LibreOJ6077前置知识:生成函数、组合数先考虑平方怎么做。\(f_{i,j}\)表示处理了前\(i\)个值,逆序对有\(j\)个。显然可以转移到\(f_{i+1,j},f_{i+1,j+1},f_{i+1,j+2},...,f_{i+1,j+i}\)。然后发现这个东西是个很简单的递推,而且长得很生成函数,于是可以很自然的写成\[1\t......
  • 题解:[SCOI2016] 美味
    前置知识:可持久化线段树(主席树)洛谷3293[SCOI2016]美味问题有一个长度为\(n\)的序列\(a_1,a_2,...,a_n\)。每次询问给你\(b\)、\(x\),你需要求出\(\max\{a_i+x\bigoplusb\}\)。\(1\lel\ler\len\le2\times10^5,0\lea_i,b,x<10^5\)首先,有\(l,r\)应该......
  • AtCoder Beginner Contest 378 题解
    AtCoderBeginnerContest378题解比赛链接A-Pairing贪心#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;voidShowball(){vector<int>a(5);for(inti=0;i<4;i++){intx;cin>>x;a[x]++;......
  • 题解:[XIX Open Cup, Grand Prix of Korea] Dev, Please Add This!
    前置知识:2-SAT题意[XIXOpenCup,GrandPrixofKorea]Dev,PleaseAddThis!在一张网格上有以下几种物品:空地(.)墙(#)星星(*)一个球(O)现在你要玩一个游戏。你可以让球朝上下左右某一个方向移动,但是一旦移动就必须走到底,也就是说直到前面是墙或者边界才能停。另外,如果你走......
  • 【题解】洛谷P7286:「EZEC-5」人赢
    P7286「EZEC-5」人赢可以想到对于每个数要找到比他大的数中下标最大的数,我们按照数的大小排序,我们维护原序列的一个指针,对于每个数如果比指针大那么就左移指针,可以思考下为什么:指针上的数比现在这个数要小那比后面的数都小,于是我们左移指针直到大于这个数,可以发现我们也在一直......
  • 【题解】洛谷P8346:最澄澈的空与海
    【题解】洛谷P8346:最澄澈的空与海猜结论题,本身其实很简单,在纸上画个差不多就能想出来,我一开始想二分图最大匹配,但是还是太大了,不可以。当一个二分图有且仅有一种解时,必定有节点的入度为\(1\)。我们想到有多种匹配的情况,可以想到如果这是一个环的情况,一个左边的点将他右边的点......
  • MX-S5 T1~T2 题解
    MX-S5题解A.王国边缘题目链接见到循环结构,先把下标转成从\(0\)开始,以方便用同余描述位置。倍增。用二元组来表示位置:\((u,v)\)表示第\(u\)个循环节的第\(v\)个位置。设\(f(i,j)\)表示从位置\((0,i)\)开始跳\(2^j\)次以后的位置。假设已经可以初始化\(f(i,......
  • CF 705 题解
    CF705题解AHulk模拟即可.BSpiderMan打sg表可以发现,奇数个球先手必败(sg=0),偶数先手必胜(sg=1).多个组合只要把sg值异或起来就好.CThor暴力模拟就可以了,用队列模拟.DAntMan结论:按照编号由小到大加入链表,每次尽量让答案最小贪心就是对的.若原来是......