您的位置:首页 > 其它

noip模拟赛 区间

2017-11-02 21:57 288 查看




分析:要遍历所有的区间,肯定是枚举左端点,然后再枚举右端点.关键是怎么高效地求区间&,|,一般而言是用前缀和的,但是&,|不满足区间可减性,所以可以考虑线段树?这道题不带修改操作,用线段树太浪费了,那么可以用ST表来维护.

查询做到O(1)了,但是怎么快速枚举区间呢?枚举左端点和右端点肯定只能选择一个优化,优化枚举右端点的循环.观察数据范围,100000,很容易想到二分.可以每次固定左端点,然后二分右端点的位置.因为&操作随着区间数的增加而答案减少,|是增加,都满足单调性,所以求满足两个条件的区间的交,统计一下区间的元素个数有多少个就可以了.

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const long long inf = 1LL << 60, mod = 1e9 + 7;

typedef long long ll;

ll n, a, b, c, d, f1[100010][22], f2[100010][22], s[100010], ans;

void init()
{
for (int j = 1; j <= 21; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
{
f1[i][j] = f1[i][j - 1] & f1[i + (1 << (j - 1))][j - 1];
f2[i][j] = f2[i][j - 1] | f2[i + (1 << (j - 1))][j - 1];
}
}

ll query(ll l, ll r,ll op)
{
ll k = (ll)((log(r - l + 1)) / log(2.0));
if (op == 1)
return f1[l][k] & f1[r - (1 << k) + 1][k];
else
return f2[l][k] | f2[r - (1 << k) + 1][k];
}

int main()
{
scanf("%lld%lld%lld%lld%lld", &n, &a, &b, &c, &d);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &s[i]);
f1[i][0] = f2[i][0] = s[i];
}
init();
for (int i = 1; i <= n; i++)
{
ll l = i, r = n, temp1 = inf, temp2 = -inf, temp3 = inf, temp4 = -inf;
while (l <= r)
{
ll mid = (l + r) >> 1;
if (query(i, mid,1) >= a)
{
l = mid + 1;
temp2 = mid;
}
else
r = mid - 1;
}

l = i, r = n;
while (l <= r)
{
ll mid = (l + r) >> 1;
if (query(i, mid, 1) <= b)
{
r = mid - 1;
temp1 = mid;
}
else
l = mid + 1;
}

l = i, r = n;
while (l <= r)
{
ll mid = (l + r) >> 1;
if (query(i, mid, 2) >= c)
{
r = mid - 1;
temp3 = mid;
}
else
l = mid + 1;
}

l = i, r = n;
while (l <= r)
{
ll mid = (l + r) >> 1;
if (query(i, mid, 2) <= d)
{
l = mid + 1;
temp4 = mid;
}
else
r = mid - 1;
}
ll ll = max(temp1, temp3), rr = min(temp2, temp4);
ans += max((long long)0, rr - ll + 1);
ans %= mod;
}
printf("%lld\n", ans);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: