CodeChef

题目链接:https://vjudge.net/problem/CodeChef-ANDMIN

Read problems statements in Mandarin ChineseRussian and Vietnamese as well.

You are given an array of N integers. You should support the following queries on this array.

  • 0 L R : Find the minimum integer in the range AL, AL+1, ..., AR.
  • 1 L R X : You should apply the assignment A[i] = A[i] & X, for all indices i in range [L, R], where & denotes bitwise AND operation.

 

Input

First line of the input contains two space separated integers N and Q.

Second line contains N integer numbers denoting array A.

In the next Q lines, each contain one of the queries described above.

Output

For each query of the type 0, output a single line containing the answer of the query.

Constraints

  • 1 ≤ N, Q ≤ 105
  • 1 ≤ AiX ≤ 109
  • 1 ≤ L ≤ R ≤ N

Example

Input:
5 5
1 5 2 3 4
0 2 5
1 1 5 6
0 2 2
1 2 5 3
0 1 3

Output:
2
4
0

题意:

给出一个序列。有两种操作:0 L R,查询区间 [L, R]的最小值,1 L R X,对区间[L,R]中的每个数与x作与操作。

题解:

1.线段树的应用。可知对于一个int类型的值,最少只需做31次与操作,每一位上的值都全部为0。

2.构架线段树,线段树中的每个结点,除了记录当前段的最小值之外,还需记录一个状态,这个状态即:这一段中,有哪些位是为1的。当此段的这个位为1,且x的这个位为0,那么就可以继续往下更新,把这个段中的这个位置为0;否则,当此段的这个位为0,而x的这个位为0,那么就无需往下更新了,因为做的是无用功。

反思:

1.自己开始时的想法是:如果这一段的数不全为0,那么就往下更新,因为期望着在如若干操作之后,这些区间的值都会变成0,那么时间限制应该不成为题。然而这只是很幼稚的想法,如果x都是1111111111,那岂不是每一次都要往下更新到根节点,但值又不发生任何改变,即做了很多无用功。

2.计算机都讲究效率,我们想只做有用功。所以,就必须清楚哪些是有用功,哪些是无用功。对于此题,如果x不能改变当前段的任何一个值,那么执行它就是无用功了。所以,在线段树的每个节点中,记录有哪些位的值为1,当x的此位也为1,那么次操作就是有用功,否则是无用功。

3.线段树的结点,就是记录当前段,或者说当前集合的公共信息,常见是最大最小值、和,当然还可以是很多天马行空的信息,要大胆想。

代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <vector>
 7 #include <queue>
 8 #include <stack>
 9 #include <map>
10 #include <string>
11 #include <set>
12 using namespace std;
13 typedef long long LL;
14 const double EPS = 1e-8;
15 const int Int_Max = (1<<31)-1;
16 const int INF = 2e9;
17 const LL LNF = 2e18;
18 const int MAXN = 1e5+10;
19 
20 int minv[MAXN<<2], state[MAXN<<2];
21 void push_up(int u)
22 {
23     minv[u] = min(minv[u*2], minv[u*2+1]);
24     state[u] = state[u*2]|state[u*2+1];     //信息集合
25 }
26 
27 void build(int u, int l, int r)
28 {
29     if(l==r)
30     {
31         scanf("%d", &minv[u]);
32         state[u] = minv[u];
33         return;
34     }
35 
36     int mid = (l+r)>>1;
37     build(u*2, l, mid);
38     build(u*2+1, mid+1, r);
39     push_up(u);
40 }
41 
42 void add(int u, int l, int r, int x, int y, int val)
43 {
44     int tmpDel = state[u]&val;  //对当前段与删除标签进行求与,得到的即为当前段可置0的位置的集合
45     if(tmpDel==0) return;   //如果没有位置可以置为0,则直接退出
46     if(l==r)
47     {
48         minv[u] ^= tmpDel;
49         state[u] ^= tmpDel;
50         return;
51     }
52 
53     int mid = (l+r)>>1;
54     if(x<=mid) add(u*2, l, mid, x, y, val);
55     if(y>=mid+1) add(u*2+1, mid+1, r, x, y, val);
56     push_up(u);
57 }
58 
59 int query(int u, int l, int r, int x, int y)
60 {
61     if(x<=l && r<=y)
62         return minv[u];
63 
64     int ret = INF;
65     int mid = (l+r)>>1;
66     if(x<=mid) ret = min(ret, query(u*2, l, mid, x, y));
67     if(y>=mid+1) ret = min(ret, query(u*2+1, mid+1, r, x, y));
68     push_up(u);
69     return ret;
70 }
71 
72 int main()
73 {
74     int n, m;
75     scanf("%d%d", &n,&m);
76     build(1,1,n);
77     int op, L, R, val;
78     while(m--)
79     {
80         scanf("%d", &op);
81         if(op==0)
82         {
83             scanf("%d%d", &L, &R);
84             printf("%d
", query(1,1,n,L,R));
85         }
86         else
87         {
88             scanf("%d%d%d", &L,&R,&val);
89             add(1,1,n,L,R,(val^Int_Max));   //对val进行按位取反。
90         }
91     }
92 }
View Code
原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8641766.html