线段树

                                            Problem D Digits Count

Accept: 11    Submit: 64
Time Limit: 10000 mSec    Memory Limit : 262144 KB

 Problem Description

Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations:

Operation 1: AND opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] AND opn (here "AND" is bitwise operation).

Operation 2: OR opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] OR opn (here "OR" is bitwise operation).

Operation 3: XOR opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] XOR opn (here "XOR" is bitwise operation).

Operation 4: SUM L R

We want to know the result of A[L]+A[L+1]+...+A[R].

Now can you solve this easy problem?

 Input

The first line of the input contains an integer T, indicating the number of test cases. (T≤100)

Then T cases, for any case, the first line has two integers n and m (1≤n≤1,000,000, 1≤m≤100,000), indicating the number of elements in A and the number of operations.

Then one line follows n integers A[0], A[1], ..., A[n-1] (0≤A[i]<16,0≤i<n).

Then m lines, each line must be one of the 4 operations above. (0≤opn≤15)

 Output

For each test case and for each "SUM" operation, please output the result with a single line.

 Sample Input

14 41 2 4 7SUM 0 2XOR 5 0 0OR 6 0 3SUM 0 2

 Sample Output

718

 Hint

A = [1 2 4 7]

SUM 0 2, result=1+2+4=7;

XOR 5 0 0, A=[4 2 4 7];

OR 6 0 3, A=[6 6 6 7];

SUM 0 2, result=6+6+6=18.


Cached at 2012-12-03 14:54:45.



解题思路:数最大为15于是可以统计2进制的个数。线段树主要是Down的时候要考虑,对于每个二进制位我考虑4种操作:&0 -> 0, |1 -> R-L+1, ^1 -> R-L+1-cnt, 其他为不变。


则代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstring>

using namespace std;

const int ROOT = 1;
const int MAXN = 1000010;
struct Node
{
    int L, R;
    int cnt[4], op[4]; 
    Node(int L =0, int R =0): L(L), R(R) { memset(op, -1, sizeof(op)); }
};
Node node[4*MAXN];
int  num[MAXN];

inline int pL(int x) { return x<<1; }
inline int pR(int x) { return (x<<1)|1; }

void BuildTree(int L, int R, int root)
{
    node[root] = Node(L, R);
    if (L == R)
    {
        for ( int i =0; i < 4; ++i) node[root].cnt[i] = num[L]&(1<<i) ? 1 : 0;
        return;
    }

    int M = (L+R)>>1;
    BuildTree(L, M, pL(root));
    BuildTree(M+1, R, pR(root));

    for ( int i =0; i < 4; i++)
        node[root].cnt[i] = node[pL(root)].cnt[i] + node[pR(root)].cnt[i];
}

inline int Cnt(Node &n, int i) { return n.op[i]==0 ? 0 : n.op[i]==1 ? n.R-n.L+1 : n.op[i]==2 ? n.R-n.L+1-n.cnt[i] : n.cnt[i]; }

void Update(int root) 
{
    Node &n = node[root];
    if (n.L == n.R)
        for ( int i =0; i < 4; n.op[i++] = -1) n.cnt[i] = Cnt(n, i);
    else
        for ( int i =0; i < 4; n.op[i++] = -1) n.cnt[i] = Cnt(node[pL(root)], i) + Cnt(node[pR(root)], i);
}

void Down(int root)
{
    Node &n = node[root];
    if (n.L != n.R)
    {
        Node &lc = node[pL(root)], &rc = node[pR(root)];
        for ( int i =0; i < 4; ++i)
            if (n.op[i] == 0 || n.op[i] == 1) lc.op[i] = rc.op[i] = n.op[i];
            else if (n.op[i] == 2)
                lc.op[i] = lc.op[i]==-1 ? 2 : 2-lc.op[i]-1,
                rc.op[i] = rc.op[i]==-1 ? 2 : 2-rc.op[i]-1;
    }
}

void Sunshine(int L, int R, int p, int m, int root)
{
    Node &n = node[root];
    Down(root);
    if (L==n.L && R==n.R)
    {
        Update(root);
        if (p == 0) for ( int i =0; i < 4; ++i) n.op[i] = (m & (1<<i))==0 ? 0 : -1;
        else if (p == 1) for ( int i =0; i < 4; ++i) n.op[i] = (m & (1<<i)) ? 1 : -1;
        else if (p == 2) for ( int i =0; i < 4; ++i) n.op[i] = (m & (1<<i)) ? 2 : -1;
        return; 
    }
    Node &lc = node[pL(root)], &rc = node[pR(root)];

    if (R <= lc.R) Sunshine(L, R, p, m, pL(root));
    else if (L >= rc.L) Sunshine(L, R, p, m, pR(root));
    else {
        Sunshine(L, lc.R, p, m, pL(root));
        Sunshine(rc.L, R, p, m, pR(root));
    }
    Update(root);
}

int Sum(Node &n)
{ 
    int res = 0; for ( int i =0; i < 4; ++i) res += Cnt(n, i) << i;
    return res;
}

int Query(int L, int R, int root)
{
    Node &n = node[root];
    Down(root); Update(root);
    if (n.L==L && n.R==R) return Sum(n);
    Node &lc = node[pL(root)], &rc = node[pR(root)];

    if (R <= lc.R) return Query(L, R, pL(root));
    if (L >= rc.L) return Query(L, R, pR(root));
    return Query(L, lc.R, pL(root)) + Query(rc.L, R, pR(root));
}

void Print(int root)
{
    Node &n = node[root];
    printf("L: %d, R: %d, %d, %d, %d, %d..\n", n.L, n.R, n.cnt[0], n.cnt[1], n.cnt[2], n.cnt[3]);
    for ( int i =0; i < 4; i++) cout << n.op[i] << " "; cout << endl;

    if (n.L == n.R) return;
    Print(pL(root));
    Print(pR(root));
}

int main()
{
    freopen("h:\\data.in", "r", stdin);
    freopen("h:\\data.out", "w", stdout);

    int T, n, m, a, b, c, op;
    for ( scanf("%d", &T); T--; )
    {
        scanf("%d%d", &n, &m);
        for ( int i =0; i < n; ++i) scanf("%d", num+i);
        BuildTree(0, n-1, ROOT);
        for ( char s[20]; m--; )
        {
            scanf("%s%d%d", s, &a, &b);
            if (strcmp(s, "SUM") == 0)
            {
                printf("%d\n", Query(a, b, ROOT));
                continue;
            }
            
            scanf("%d", &c);
            op = strcmp(s, "AND")==0 ? 0 : strcmp(s, "OR")==0 ? 1 : 2;
            Sunshine(b, c, op, a, ROOT);
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值