传送门 ↬
题目描述
小 a 有 n 个数 a1,a2,...,an,给出 q 个询问,每次询问给出区间 [l,r],现在请你找到一个数 x,使得 i=l∑rx⊕ai最大,要保证 0≤x<231。
输入描述
第一行一个整数 n,表示序列的长度。第二行 n 个整数,表示序列内的元素。第三行一个整数 q,表示询问的个数。接下来 q 行,每行两个整数 l,r,表示询问的区间。
输出描述
输出 q 行,每行一个整数表示答案。若有多组可行解,请输出较小的解。
示例1
输入
5
4 78 12 1 3
3
2 5
1 4
3 3
输出
2147483632
2147483635
2147483635
备注
对于 $30\%$ 的数据,n,q≤10。对于 $60\%$ 的数据,n,q≤1000。对于 $100\%$ 的数据,n,q≤105。保证 ai$ <$ 231。
分析
将所有数拆成 31 位二进制数。对于一个区间 [l,r],若 $x $ 的第 j 位为 1,且 [l,r] 内第 j 位为 0 的数字个数为 seg0,那么对和式 i=l∑rx⊕ai 产生的贡献为 seg0×2j;同理,若 x 的第 j 位为 0 ,且 [l,r] 内第 j 位为 1 的数字个数为 seg1,那么对和式 i=l∑rx⊕ai 产生的贡献为 seg1×2j。
seg0×2j>seg1×2j 的充要条件是 seg0>seg1。每次询问一个区间 [l,r],我们依次考察 [l,r] 内所有数的第 0∼30 位,若某一位 0 居多,则 x 在该位取 1;否则,x 在该位取 0。
显然,可以预处理某一位 1 个数的前缀和,考察一个区间时,可以 O(1) 得到答案。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include<iostream> #include<cstdio> #define N 100003 using namespace std;
int sum[N][32];
int q; int n; int main() { cin>>n; int i,j; for(i=1;i<=n;i++) { int temp; scanf("%d",&temp); for(j=0;j<=30;j++) { if(temp&(1<<j)) sum[i][j]=1; else sum[i][j]=0; } } for(i=1;i<=n;i++) { for(j=0;j<=30;j++) { sum[i][j]+=sum[i-1][j]; } } cin>>q; while(q--) { int l,r; int x=0; scanf("%d%d",&l,&r); for(j=0;j<=30;j++) { int seg1=sum[r][j]-sum[l-1][j]; int seg0=r-l+1-seg1; if(seg0>seg1) x=x+(1<<j); } printf("%d\n",x); } return 0; }
|