雅礼联考10-27 c 线段树
Posted brunch
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了雅礼联考10-27 c 线段树相关的知识,希望对你有一定的参考价值。
题意
- 给你一个序列,(q)次询问一个区间内有多少个子区间满足自区间内所有的数进行按位与运算的结果是完全平方数
又是(Lstete)给我讲的...
他不久前给我讲的东西我又不会了我是真的捞
这种求区间内的子区间的题目
一般为了避免算重,可以考虑把询问离线挂在右端点上
我们首先可以发现一个性质
一个数进行按位与运算后结果一定不会变大
意思就是对于一个数不停进行与运算,只有(log)种取值
我们考虑一个数作为右端点的区间
这些区间进行按位与运算的取值只有(log)种
并且取值相同的区间都是连续的
那么我们对于取值相同的区间可以一并修改
那么只要预处理出以每个数结尾二进制位为(1)的位中
离它最近的并且那一位为(0)的数的位置
就可以找到这些区间的断点了
这样做的复杂度是(O(qlognlog1e9))的
所以这题需要卡常.....
Codes
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
const int M = 30 + 1;
unordered_map<int, bool> is;
vector<pair<int, int>> Q[N];
long long ans[N << 1];
int a[N], pre[M][N];
struct Segment_Tree
{
#define mid ((l + r) >> 1)
#define ls (bh << 1)
#define rs (ls | 1)
#define lson ls, l, mid
#define rson rs, mid + 1, r
long long S[N << 2], lazy[N << 2];
void modify(int bh, int l, int r, long long val)
{
lazy[bh] += val;
S[bh] += (r - l + 1) * val;
}
void pushup(int bh)
{
S[bh] = S[ls] + S[rs];
}
void pushdown(int bh, int l, int r)
{
if(lazy[bh])
{
modify(lson, lazy[bh]);
modify(rson, lazy[bh]);
lazy[bh] = 0;
}
}
void build(int bh, int l, int r)
{
S[bh] = lazy[bh] = 0;
if(l ^ r) build(lson), build(rson);
}
void update(int bh, int l, int r, int x, int y, long long z)
{
if(x <= l && r <= y)
modify(bh, l, r, z);
else
{
pushdown(bh, l, r);
if(x <= mid) update(lson, x, y, z);
if(y > mid) update(rson, x, y, z);
pushup(bh);
}
}
long long query(int bh, int l, int r, int x, int y)
{
long long res = 0;
if(x <= l && r <= y)
res += S[bh];
else
{
pushdown(bh, l, r);
if(x <= mid) res += query(lson, x, y);
if(y > mid) res += query(rson, x, y);
}
return res;
}
}T;
int main()
{
//freopen("c.in", "r", stdin);
//freopen("c.out", "w", stdout);
int t, n, q, x, y;
for(long long i = 0; i * i <= INT_MAX; ++ i)
is[i * i] = true;
for(scanf("%d", &t); t -- ; )
{
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; ++ i)
scanf("%d", &a[i]);
for(int i = 1; i <= q; ++ i)
{
scanf("%d%d", &x, &y);
Q[y].push_back(make_pair(i, x));
}
T.build(1, 1, n);
for(int i = 1; i < n; ++ i)
for(int j = 0; j < M; ++ j)
{
if(a[i] >> j & 1)
pre[j][i + 1] = pre[j][i];
else
pre[j][i + 1] = i;
}
for(int r = 1; r <= n; ++ r)
{
int val = a[r], pos = r;
while(pos)
{
int last = 0;
for(int j = 0; j < M; ++ j)
if(val >> j & 1)
last = max(last, pre[j][pos]);
if(is[val])
T.update(1, 1, n, last + 1, pos, 1);
pos = last, val &= a[last];
}
for(; !Q[r].empty(); Q[r].pop_back())
{
pair<int, int> now = Q[r].back();
ans[now.first] = T.query(1, 1, n, now.second, r);
}
}
memset(pre, 0, sizeof(pre));
for(int i = 1; i <= q; ++ i)
printf("%lld
", ans[i]);
}
return 0;
}
以上是关于雅礼联考10-27 c 线段树的主要内容,如果未能解决你的问题,请参考以下文章
loj6029「雅礼集训 2017 Day1」市场 线段树+均摊分析