1. 程式人生 > >雅禮聯考10-27 c 線段樹

雅禮聯考10-27 c 線段樹

pty 題目 做的 lin uil map 位與 turn namespace

題意

  • 給你一個序列,\(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\n", ans[i]);
    }

    return 0;
}

雅禮聯考10-27 c 線段樹