noip模拟赛 区间

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

 

posted @ 2017-11-02 21:57  zbtrs  阅读(249)  评论(0编辑  收藏  举报