I love counting HDU - 6964
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了I love counting HDU - 6964相关的知识,希望对你有一定的参考价值。
题意:
一个数组c,给你了(l,r)一个范围,问这个范围内满足ci ^ a < b数量的有多少?
题解:
我第一反应是莫队,直接莫队得到结果,但是发现样例不对,再调了半天后我突然想明白,对于每个询问a和b是不一样的,也就是说莫队是通过询问来调整区间大小,上一次询问满足情况的答案不一定适用于下一个询问,所以每次都要重新询问,所以就不是简单的莫队
方法一:
不能直接莫队,那我们就改改,用莫队+分块来做,我们用莫队维护每次询问的每个块内元素种类,以及每个元素本身的数量
然后我们开始单独分析式子c ^ a <= b
对于等号情况我们单独考虑,先只考虑小于号
对于第j位
如果b是1,a是1,那么c是1,一定小于b
如果b是1,a是0,那么c是0,一定小于b
如果b是0,a是1,那么c只能是1才有可能小于b
如果b是0,a是0,那么c只能是0才有可能小于b
我们统计一定小于的情况,不断向后找小于的情况。用分块来计算小于的情况,比如c的第三位是1,例如0100,那么从0100到0111都是满足题意的,我们用一个前缀和来计算这个区间。前缀和内是用分块,之前我们已经统计了每个块的大小,还有每个元素的数量。
最后还有c ^ a =b的情况,这简单c = a ^ b,看是否存在这个c就完事了
相当于莫队都预处理好,给后面直接用
方法二:
刚才我分析第j位的那些步骤,不正是字典树的步骤,所以这个题也可以用字典树来做,用树状数组维护前缀和
具体做法:
用vector存每个右端点所对应的当前询问对应的l,a,b,用vis数组记录当前值是否出现过,用树状数组维护前缀,答案就是cal(i)-cal(l-1)
这里树状数组的作用单纯就是算区间和
代码:
莫队+分块
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 100010;
int n, m, a[N], k, cnt[N], sum[N], c[2*N], ans[N];//c要开2N,因为2个1e5的数异或值可以大于1e5
// sum[i]表示块内数的种类,c[i]表示每个i这个数的种类
struct query {
int l, r, a, b, id;
bool operator<(const query &b) const {
if (l / k == b.l / k)return r < b.r;
return l < b.l;
}
} q[N];
void add(int x) {
if (++cnt[x] == 1)sum[x / k]++, c[x]++;
}
void sub(int x) {
if (--cnt[x] == 0)sum[x / k]--, c[x]--;
}
int ask(int x) {
int res = 0;
for (int i = x / k * k; i <= x; i++)res += c[i];
for (int i = 0; i < x / k; i++)res += sum[i];
return res;
}
int main() {
scanf("%d", &n);
k = sqrt(n);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d%d%d%d", &q[i].l, &q[i].r, &q[i].a, &q[i].b);
q[i].id = i;
}
sort(q, q + m);
int l = 1, r = 0;
for (int i = 0; i < m; i++) {
while (l > q[i].l)add(a[--l]);
while (l < q[i].l)sub(a[l++]);
while (r < q[i].r)add(a[++r]);
while (r > q[i].r)sub(a[r--]);
int s = 0;
int a = q[i].a, b = q[i].b;
for (int j = 19; j >= 0; j--) {
int p=s;
if (b >> j & 1) {
if (a >> j & 1)p |= 1 << j;
else s |= 1 << j;
ans[q[i].id] += ask(p + (1 << j) - 1) - ask(p - 1);//第j位是1的答案数量
}
else
{
if (a >> j & 1)
s |= 1 << j;
}
}
ans[q[i].id] += c[q[i].a ^ q[i].b];
}
for (int i = 0; i < m; i++)printf("%d\\n", ans[i]);
return 0;
}
字典树+线段树
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 1e5 + 10;
int tr[400 * N][2], rt[N], sum[400 * N], ans[N], pre[N], idx;
int n, a[N], m;
struct node {
int l, a, b, id;
} q[N];
vector<node> v[N];
void insert(int &now, int x, int val) {
if (!now)now = ++idx;
int u = now;
for (int i = 17; ~i; i--) {
int p = val >> i & 1;
if (!tr[u][p])tr[u][p] = ++idx;
u = tr[u][p];
sum[u] += x;
}
}
void add(int pos, int x, int val) {
for (int i = pos; i <= n; i += (i & -i))insert(rt[i], x, val);
}
int query(int u, int a, int b) {
int res = 0;
for (int i = 17; ~i; i--) {
int p1 = a >> i & 1;
int p2 = b >> i & 1;
if (p2) {
if (p1)res += sum[tr[u][1]], u = tr[u][0];
else res += sum[tr[u][0]], u = tr[u][1];
} else {
if (p1)u = tr[u][1];
else u = tr[u][0];
}
if(!u)break;
}
return res+sum[u];
}
int ask(int p, int a, int b) {
int res = 0;
for (int i = p; i; i -= (i & -i))res += query(rt[i], a, b);
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
scanf("%d", &m);
for (int i = 0; i < m; i++) {
int l, r, a, b;
scanf("%d%d%d%d", &l, &r, &a, &b);
v[r].push_back({l, a, b, i});
}
for (int i = 1; i <= n; i++) {
if (pre[a[i]])add(pre[a[i]], -1, a[i]);
add(i, 1, a[i]);
pre[a[i]] = i;
for (auto j:v[i])
ans[j.id] = ask(i, j.a, j.b) - ask(j.l - 1, j.a, j.b);
}
for (int i = 0; i < m; i++)printf("%d\\n", ans[i]);
return 0;
}
以上是关于I love counting HDU - 6964的主要内容,如果未能解决你的问题,请参考以下文章
2021HDU多校第二场 1004 I love counting 树状数组套01trie