51NOD 1674 区间的价值 V2

题意

给n个数 a1 ~ an1ai1e9 ,求 [1lrn][al&al+1&...&ar][al|al+1|...|ar] (结果对le9+7取模)

思路

[1lrn](al&al+1&...&ar)(al|al+1|...|ar)

=[1lrn][0x<30]2x[axl&...&axr][0y30]2y[ayl|...|ayr]

=[0x30][0y30]2x+y[1lrn][axl&...&axr][ayl|...|ayr]

=[0x30][0y30]2x+y[1lrn][axl&...&axr](1[(!al)y&...&(!ar)y])

=[0x30][0y30]2x+y[1lrn][axl&...&axr][ayl|...|ayr]

=[0x30][0y30]2x+y[1lrn]([axl&...&axr][axl&(!al)y&...&axr&(!ar)y])

枚举 x,y ,令 bi=axi ci=axi&(!ai)y ,所以
[1lrn](al&al+1&...&ar)(al|al+1|...|ar)

=[0x30][0y30]2x+y[1lrn]([bl&...&br][cl&...&cr])

=[0x30][0y30]2x+y([1lrn][bl&...&br][1lrn][cl&...&cr])

所以只需要求一个01数组 ai 内,满足 al ~ ar 之间全为1的 (l,r) ,假设 ai 里面的连续的1的个数依次为 c1,c2... 那么答案就是 c1(c1+1)2+c2(c2+1)2+... ,直接按段统计复杂度是 O(nlogXlogX )的,优化办法是,用20位整数把a压缩,这样size可以降低20倍,复杂度接近 O(nlogX)

代码

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#include "local.h"
#endif // LOCAL
typedef long long ll;
const ll mod = 1e9 + 7;
const ll N = 1e5 + 7;
ll n, a[N], b[N], c[N], pow2[77], Log2[1 << 22], t[1 << 21], pre[1 << 21], suf[1 << 21];
inline ll lowbit(ll x) {
  return x & -x;
}
inline ll calc(ll x) {
  return x * (x + 1) / 2;
}
ll calc(vector<ll> &a) {
  ll n = a.size(), buf = 0, ans = 0;
  for (ll i = 0; i < n; i++) {
    buf += pre[a[i]];
    if (pre[a[i]] != 20) {
      ans += calc(buf);
      ans += t[a[i]] - calc(pre[a[i]]) - calc(suf[a[i]]);
      buf = suf[a[i]];
    }
  }
  ans += calc(buf);
  return ans;
}
ll calc(vector<ll> &a, vector<ll> &b) {
  ll n = a.size();
  vector<ll> c(n);
  for (ll i = 0; i < n; i++) {
    c[i] = a[i] & ~b[i];
  }
  return calc(c);
}
vector<ll> bl[30];
int main() {
  pow2[0] = 1;
  for (ll i = 1; i < 77; i++) pow2[i] = pow2[i - 1] * 2 % mod;
  for (ll i = 0; i < 22; i++) Log2[1 << i] = i;
  for (ll i = 1; i < (1 << 21); i++) {
    ll lastb1 = Log2[lowbit(i)], lastb2 = Log2[lowbit(i + lowbit(i))], lastl = lastb2 - lastb1, lastv = pow2[lastb2] - pow2[lastb1];
    t[i] = t[i - lastv] + calc(lastl);
    if (i & 1) suf[i] = lastl;
    if ((i >> 19) & 1) pre[i] = pre[(i - (1 << 19)) << 1] + 1;
  }
  cin >> n;
  for (ll i = 0; i < n; i++) {
    scanf("%lld", a + i);
  }
  ll ans = 0;
  for (ll i = 0; i < 30; i++) {
    bl[i].resize(n / 20 + (n % 20 > 0));
  }
  for (ll i = 0; i < 30; i++) {
    for (ll id = 0; id < n; id++) {
      if (id % 20 == 0) bl[i][id / 20] = 0;
      ll &p = bl[i][id / 20];
      p = p * 2 + ((a[id] >> i) & 1);
    }
    bl[i][bl[i].size() - 1] <<= (20 - n % 20) % 20;
  }
  for (ll i = 0; i < 30; i++) {
    ll buf = calc(bl[i]);
    for (ll j = 0; j < 30; j++) {
      ans = (ans + pow2[i + j] * (buf - calc(bl[i], bl[j]))) % mod;
    }
  }
  cout << ans << endl;
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值