牛客练习赛77 D-小G的LY数对 思维+Hash

牛客练习赛77 D-小G的LY数对 思维+Hash


传送门: https://ac.nowcoder.com/acm/contest/11160/D

题意

小G定义LY数对为两个数x,y在二进制的异或操作后恰好有两位是1
小G现在有两个数组a,b长度分别为n,m
现在小G想知道有多少对i,j满足
(1<=i<=n,1<=j<=m)满足a[i]和b[j]是LY数对

思路

如 果 是 暴 力 枚 举 a [ i ] 的 同 时 暴 力 枚 举 两 个 位 置 上 的 数 , 复 杂 度 为 O ( 3 0 2 n ) 。 如果是暴力枚举a[i]的同时暴力枚举两个位置上的数,复杂度为O(30^2n)。 a[i]O(302n)
必 然 会 T , 怎 么 办 能 把 其 中 一 个 30 变 小 呢 ? 直 到 复 杂 度 在 O ( 1 e 8 ) 之 内 ? 必然会T,怎么办能把其中一个30变小呢?直到复杂度在O(1e8)之内? T30O(1e8)

我 们 知 道 题 目 的 意 思 是 x ⨁ y = 两 位 2 次 方 数 相 加 = ( 1 < < i ) ⨁ ( 1 < < j ) . ( i ! = j ) 我们知道题目的意思是x \bigoplus y=两位2次方数相加=(1<<i)\bigoplus (1<<j).(\red{i!=j}) xy=2=(1<<i)(1<<j).(i!=j)

转 化 为 x ⨁ ( 1 < < i ) = y ⨁ ( 1 < < j ) . ( i ! = j ) 转化为x\bigoplus (1<<i)=y\bigoplus (1<<j).(\red{i!=j}) x(1<<i)=y(1<<j).(i!=j)
转 化 为 x ⨁ ( 1 < < j ) = y ⨁ ( 1 < < i ) . ( i ! = j ) 转化为x\bigoplus (1<<j)=y\bigoplus (1<<i).(\red{i!=j}) x(1<<j)=y(1<<i).(i!=j)

所 以 我 们 可 以 通 过 这 个 。 所以我们可以通过这个。
枚 举 i , H a s h 保 存 所 有 的 a x ⨁ ( 1 < < i ) . 枚举i,Hash保存所有的a_x\bigoplus (1<<i). iHashax(1<<i).
枚 举 j 所 有 的 b y ⨁ ( 1 < < j ) , 查 找 是 否 有 和 它 相 等 的 数 , a n s + 个 数 。 枚举j所有的b_y\bigoplus (1<<j),查找是否有和它相等的数,ans+个数。 jby(1<<j)ans+
$$

可 是 我 们 之 前 说 过 i ! = j , 所 以 要 提 前 减 掉 这 个 i = = j 的 情 况 。 可是我们之前说过\red{i!=j},所以要提前减掉这个i==j的情况。 i!=ji==j
i = = j 必 然 x = = y , 所 以 减 掉 x = = y 的 数 量 即 可 。 i==j必然x==y,所以减掉x==y的数量即可。 i==jx==yx==y

总 时 间 复 杂 度 为 O ( 30 n ∗ C ) , C 是 一 个 小 于 30 的 常 数 。 总时间复杂度为O(30n*C),C是一个小于30的常数。 O(30nC),C30

Code(690MS)

#include "bits/stdc++.h"
using namespace std;

typedef long long ll;

const int N = 1e7 + 7;
const int mod = 1e6 + 7;

struct Hash {
    int v, next, w;
}e[N];
int head[N], cnt;

void Insert(int x) {
    int u = x % mod;
    for(int i = head[u]; ~i; i = e[i].next) {
        if(e[i].v == x) {
            e[i].w++;
            return ;
        }
    }
    e[cnt] = Hash{x, head[u], 1};
    head[u] = cnt++;
}

int get(int x) {
    int u = x % mod;
    for(int i = head[u]; ~i; i = e[i].next) {
        if(e[i].v == x) {
            return e[i].w;
        }
    }
    return 0;
}

map<int, int> mp;

void solve() {
    mem(head, -1);
    int n, m; cin >> n >> m;
    vector<int> a(n + 1), b(m + 1);
    for(int i = 1;i <= n; i++) cin >> a[i], mp[a[i]]++;
    ll res = 0;
    for(int i = 1;i <= m; i++) {
        cin >> b[i];
        res += mp[b[i]] * 30;
    }
    for(int i = 1;i <= n; i++) {
        for(int k = 0;k < 30; k++) {
            Insert(a[i] ^ (1ll << k));
        }
    }
    ll ans = 0;
    for(int i = 1;i <= m; i++) {
        for(int k = 0;k < 30; k++) {
            ans += get(b[i] ^ (1ll << k));
        }
    }
    cout << (ans - res) / 2 << endl;
}

signed main() {
    solve();
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值