CodeForces - 1004F Sonya and Bitwise OR(线段树区间合并)

F. Sonya and Bitwise OR

time limit per test

4 seconds

memory limit per test

512 megabytes

input

standard input

output

standard output

Sonya has an array a1,a2,…,ana1,a2,…,an consisting of nn integers and also one non-negative integer xx. She has to perform mm queries of two types:

  • 11 ii yy: replace ii-th element by value yy, i.e. to perform an operation aiai := yy;
  • 22 ll rr: find the number of pairs (LL, RR) that l≤L≤R≤rl≤L≤R≤r and bitwise OR of all integers in the range [L,R][L,R] is at least xx (note that xx is a constant for all queries).

Can you help Sonya perform all her queries?

Bitwise OR is a binary operation on a pair of non-negative integers. To calculate the bitwise OR of two numbers, you need to write both numbers in binary notation. The result is a number, in binary, which contains a one in each digit if there is a one in the binary notation of at least one of the two numbers. For example, 1010 OR 1919 = 1010210102 OR 100112100112 = 110112110112 = 2727.

Input

The first line contains three integers nn, mm, and xx (1≤n,m≤1051≤n,m≤105, 0≤x<2200≤x<220) — the number of numbers, the number of queries, and the constant for all queries.

The second line contains nn integers a1,a2,…,ana1,a2,…,an (0≤ai<2200≤ai<220) — numbers of the array.

The following mm lines each describe an query. A line has one of the following formats:

  • 11 ii yy (1≤i≤n1≤i≤n, 0≤y<2200≤y<220), meaning that you have to replace aiai by yy;
  • 22 ll rr (1≤l≤r≤n1≤l≤r≤n), meaning that you have to find the number of subarrays on the segment from ll to rr that the bitwise OR of all numbers there is at least xx.

Output

For each query of type 2, print the number of subarrays such that the bitwise OR of all the numbers in the range is at least xx.

Examples

input

Copy

4 8 7
0 3 6 1
2 1 4
2 3 4
1 1 7
2 1 4
2 1 3
2 1 1
1 3 0
2 1 4

output

Copy

5
1
7
4
1
4

input

Copy

5 5 7
6 0 3 15 2
2 1 5
1 4 4
2 1 5
2 3 5
2 1 4

output

Copy

9
7
2
4

Note

In the first example, there are an array [00, 33, 66, 11] and queries:

  1. on the segment [1…41…4], you can choose pairs (11, 33), (11, 44), (22, 33), (22, 44), and (33, 44);
  2. on the segment [3…43…4], you can choose pair (33, 44);
  3. the first number is being replacing by 77, after this operation, the array will consist of [77, 33, 66, 11];
  4. on the segment [1…41…4], you can choose pairs (11, 11), (11, 22), (11, 33), (11, 44), (22, 33), (22, 44), and (33, 44);
  5. on the segment [1…31…3], you can choose pairs (11, 11), (11, 22), (11, 33), and (22, 33);
  6. on the segment [1…11…1], you can choose pair (11, 11);
  7. the third number is being replacing by 00, after this operation, the array will consist of [77, 33, 00, 11];
  8. on the segment [1…41…4], you can choose pairs (11, 11), (11, 22), (11, 33), and (11, 44).

In the second example, there are an array [66, 00, 33, 1515, 22] are queries:

  1. on the segment [1…51…5], you can choose pairs (11, 33), (11, 44), (11, 55), (22, 44), (22, 55), (33, 44), (33, 55), (44, 44), and (44, 55);
  2. the fourth number is being replacing by 44, after this operation, the array will consist of [66, 00, 33, 44, 22];
  3. on the segment [1…51…5], you can choose pairs (11, 33), (11, 44), (11, 55), (22, 44), (22, 55), (33, 44), and (33, 55);
  4. on the segment [3…53…5], you can choose pairs (33, 44) and (33, 55);
  5. on the segment [1…41…4], you can choose pairs (11, 33), (11, 44), (22, 44), and (33, 44).

 

题意:给你N个数,M个询问,支持单点更新,区间查询,区间或和>=X的对数。

 

解题思路:线段树区间合并。由于是或,所以对于一个区间而言,他从前往后跟从后往前组成的数字不会很多。所以完全可以暴力的区间合并。对于线段树上的节点,维护当前ans,和前缀或的种类和数量,后缀或的种类和数量。然后合并的时候。

左节点的后缀 跟 右节点的前缀 ,暴力合并即可。

然后要更新当前节点的后缀和前缀。先让当前节点的后缀等于右节点的后缀,然后暴力左节点的所有后缀跟当前节点的后缀的最后一个的或,插入到当前节点中即可。根据或的性质,值只会越来越大,所以这么做是没问题的。

 

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int MAXN = 100005;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> pii;
int a[MAXN];

struct node{
    ll sum;
    vector<pii> suff;
    vector<pii> pref;
    node(){
        sum=0;
    }
}t[MAXN<<2];
int N,M,X;

//加了个引用,快了1s钟
ll getans(node &r1,node &r2){
    ll ans=0;
    //两个for暴力,完全没问题,因为数字种类不会很多
    for(pii &i:r1.suff){
        for(pii &j:r2.pref){
            if((i.second|j.second)>=X)
                ans+=ll(i.first)*ll(j.first);
        }
    }
    return ans;
}

//加了个引用,快了1s钟
node pushup(node &r1,node &r2){
    if(r1.pref.size()==0)
        return r2;
    if(r2.pref.size()==0)
        return r1;

    node ans;
    ans.sum=r1.sum+r2.sum;
    ans.sum+=getans(r1,r2);

    //暴力更新当前节点后缀(从后往前读的那种后缀)
    ans.suff=r2.suff;
    for(pii &i:r1.suff)
    {
        pii b=r2.suff.back();
        if((i.second|b.second)==ans.suff.back().second){
           ans.suff.back().first+=i.first;
        }
        else{
            ans.suff.push_back({i.first,i.second|b.second});
        }
    }
    
    //暴力更新当前节点前缀
    ans.pref=r1.pref;
    for(pii &i:r2.pref)
    {
        pii b=r1.pref.back();
        if((i.second|b.second)==ans.pref.back().second){
            ans.pref.back().first+=i.first;
        }
        else{
            ans.pref.push_back({i.first,i.second|b.second});
        }
    }
    return ans;
}

void build(int l,int r,int rt){
    if(l==r){
        if(a[l]>=X)
            t[rt].sum=1;
        t[rt].pref.push_back({1,a[l]});
        t[rt].suff.push_back({1,a[l]});
        return;
    }
    int m=(l+r)/2;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    t[rt]=pushup(t[rt<<1],t[rt<<1|1]);
}

void update(int L,int C,int l,int r,int rt){
    if(l==r){
        if(C>=X)
            t[rt].sum=1;
        else
            t[rt].sum=0;
        a[l]=C;
        t[rt].pref.clear();
        t[rt].suff.clear();
        t[rt].pref.push_back({1,C});
        t[rt].suff.push_back({1,C});
        return;
    }
    int m=(l+r)/2;
    if(L<=m)
        update(L,C,l,m,rt<<1);
    else
        update(L,C,m+1,r,rt<<1|1);
    t[rt]=pushup(t[rt<<1],t[rt<<1|1]);
}

node query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R)
        return t[rt];
    int m=(l+r)/2;
    node ans;
    if(L<=m){
        node t=query(L,R,l,m,rt<<1);
        ans=pushup(t,ans);
    }
    if(R>m){
        node t=query(L,R,m+1,r,rt<<1|1);
        ans=pushup(ans,t);//注意,查询的时候答案也要合并。
    }
    return ans;
}

int main()
{
    scanf("%d%d%d",&N,&M,&X);
    for(int i=1;i<=N;i++)
        scanf("%d",&a[i]);
    build(1,N,1);
    int op,u,v;
    while(M--){
        scanf("%d%d%d",&op,&u,&v);
        if(op==1)
            update(u,v,1,N,1);
        else{
            printf("%lld\n",query(u,v,1,N,1).sum);
        }
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值