I love counting HDU - 6964

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了I love counting HDU - 6964相关的知识,希望对你有一定的参考价值。

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

hdu 5656 CA Loves GCD(dp)

hdu 5646 DZY Loves Partition

hdu6599 I Love Palindrome String

HDU5269 ZYB loves Xor I

HDU-3033 I love sneakers! (分组背包)