Divan and bitwise operations 异或,同或,组合数学(1500)

在这里插入图片描述
题意 :

  • 原序列长为n,给m个连续子序列的左右端点的下标以及该连续子序列的或和,保证这m个连续子序列的并集包含原序列每一个点,求原序列的所有子序列的异或和,输出任意一种

思路 :

  • 由于位运算对每个位都是独立的,考虑分别对每一位求解
  • 首先看输入的给我们带来什么,对于[l,r]得到的同或和x,考虑它的某一位,如果这一位是0,说明[l,r]每个元素这位都是0,如果这一位是1,至少有1个1,后者没什么用
  • 考虑某一位bit,一共有n个元素,假设有a个1,b个0,显然有 a + b = n a+b=n a+b=n
  • 我们如果要对结果(所有子序列异或和的总和),就要选出若干个元素使它们异或和为1,根据异或和的性质,需要选择奇数个1和若干个(0)0
  • 选奇数个1的方案为 C a 1 + C a 3 + . . . = 2 a − 1 C_{a}^{1}+C_{a}^{3}+... =2^{a-1} Ca1+Ca3+...=2a1,选任意个0的方案为 2 b 2^{b} 2b
  • 根据乘法原理,总方案数为 2 a − 1 ∗ 2 b = 2 a + b − 1 = 2 n − 1 2^{a-1}*2^b=2^{a+b-1}=2^{n-1} 2a12b=2a+b1=2n1,在当前bit位上贡献为 1 < < b i t 1<<bit 1<<bit
  • 所以对于每个位,只要有1,那么这个位对结果的贡献就是 2 n − 1 ∗ ( 1 < < b i t ) 2^{n-1}*(1 << bit) 2n1(1<<bit),否则这个位对结果的贡献是0
  • 假设bit位存在1,那么v中就可以拆除一块(1 << bit),我们按照这种方法把v拆成若干个块,每个块都能产生这么多贡献,因此可以直接用v乘上 2 n − 1 2^{n-1} 2n1得到答案
  • 即,只要看哪些位有1, ( 1 < < b i t ) ∗ 2 n − 1 (1<<bit)*2^{n-1} (1<<bit)2n1即可,事实上,设置一个初始变量v为00…0,每次读入一个区间,都能知道哪些位现在至少有一个1,这个位可以被累加答案了,v|=x
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <unordered_set>
#include <math.h>
#define endl '\n'
#define fi first
#define se second
#define pb push_back

using namespace std;
using ll = long long;

typedef pair<int, int> PII;

const ll mod = 1e9 + 7;

void solve()
{
    int n, m, v = 0; cin >> n >> m;
    for (int i = 1, l, r, x; i <= m && cin >> l >> r >> x; i ++ ) v |= x;
    
    ll fac = 1;
    for (int i = 1; i <= n - 1; i ++ ) fac = fac * 2ll % mod;
    cout << fac * v % mod << endl;
}

int main()
{
    cin.tie(nullptr) -> sync_with_stdio(false);
    
    int _;
    cin >> _;
    while (_ -- )
        solve();
    
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值