莫队 ---- 值域分块处理优化字典树上的查询 杭电多校2021第二场1004 i love counting

题目链接


题目大意:

就是给你 n n n个数, m m m次询问
每次询问给你 l , r , a , b l,r,a,b l,r,a,b问你在 [ l , r ] [l,r] [l,r]区间里面有多少 种 种 c ( X O R ) a ≤ b c(XOR)a\leq b c(XOR)ab


解题思路:

这里我们对两个东西进行分块,首先是查询区间分块,然后按照块排序就是莫队了,但是值域怎么维护呢?

但是我们是要查询里面种类数哇,那么我们就要对区间里面的数进行去重,排序,那么我们可以为了和莫队进行配合最好就是分块

我们对值域进行分块把 [ 1 , 1 e 5 ] [1,1e5] [1,1e5]的值域分成 1 e 5 \sqrt {1e5} 1e5 块,每个块统计里面的数的种类!!

但是如何查询异或满足上面条件数的个数呢?

就是我们假设是在字典树上面查询的对 a , b a,b a,b进行按位进行观察
如 果 b 的 第 b i t 位 是 1 如果b的第bit位是1 bbit1

  1. 如果 c 的 b i t 位 和 a 的 b i t 为 不 同 c的bit位和a的bit为不同 cbitabit,那么我们还不能更新答案。
  2. 如果 c 的 b i t 位 和 a 的 b i t 为 相 同 c的bit位和a的bit为相同 cbitabit,那么这一位都比b小了,那么后面怎么取都可以直接去去分块整体进行区间查询
  3. 固定已经确定的高位,继续往低位看

如 果 b 的 第 b i t 位 是 0 如果b的第bit位是0 bbit0


#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x)
{
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
   read(first);
   read(args...);
}

struct node {
   int l, r, id;
   int block;
   int a, b;
   bool operator < (const node & y) const {
      if(block ^ y.block) return block < y.block;
      else if(block & 1) return r < y.r;
      else return r > y.r;
   }
}q[maxn];
int blockqu, blockdate = 316, sz = 17;
int arr[maxn];
int sum[maxn], bl[maxn];
int ans[maxn];
inline void Add(int u) {
   if(++ sum[u] == 1) bl[u/blockdate] ++;//值域分块统计每个数的种类
}
inline void Sub(int u) {
   if(-- sum[u] == 0) bl[u/blockdate] --;
}

inline int slove(int l, int r) {
   int blockl = l/blockdate;
   int blockr = r/blockdate;
   int ans = 0;
   if(blockl == blockr) {
      for(int i = l; i <= r; ++ i)
        ans += (sum[i] > 0);
      return ans;
   } 
   for(int i = blockl+1; i <= blockr-1; ++ i)
     ans += bl[i];
   for(int i = l; i <= (blockl+1)*blockdate-1; i ++)
     ans += (sum[i] > 0);
   for(int i = blockr*blockdate; i <= r; ++ i)
     ans += (sum[i] > 0);
   return ans;
}

inline int cal(int a, int b) {
    int hightbit = 0;//统计已经确定的高位
    int res = (sum[a^b] > 0);//如果一直递归到底sum[a^b]是没有被计算的
    for(int i = sz; i >= 0; -- i) {
       int bita = (a >> i) & 1;
       int bitb = (b >> i) & 1;
       if(bitb) {
       //按照字典树上面的查询方式,如果沿着一个方向跑那么高位是被确定好的
          int segl = hightbit + (bita ? 1 << i:0);
          int segr = segl + (1 << i) - 1;
          res += slove(segl,segr);
       }
       if(bita ^ bitb) hightbit |= (1 << i);
    }
    return res;
}

int main() {
    IOS;
    int n;
    cin >> n;
    for(int i = 1; i <= n; ++ i) cin >> arr[i];
    int m;
    cin >> m;
    blockqu = sqrt(m)+1;
    for(int i = 1; i <= m; ++ i) {
        int l, r, a, b;
        cin >> l >> r >> a >> b;
        q[i] = {l,r,i,(l-1)/blockqu+1,a,b};
    }
    sort(q+1,q+1+m);
    int l = 1, r = 0;
    for(int i = 1; i <= m; ++ i) {
       while(q[i].l < l) Add(arr[--l]);
       while(q[i].r > r) Add(arr[++r]);
       while(q[i].l > l) Sub(arr[l++]);
       while(q[i].r < r) Sub(arr[r--]);
       ans[q[i].id] = cal(q[i].a,q[i].b);
    }
    for(int i = 1; i <= m; ++ i)
      cout << ans[i] << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值