题目描述:
You are given n numbers a1, a2, ..., an. You can perform at most k operations. For each operation you can multiply one of the numbers by x. We want to make as large as possible, where denotes the bitwise OR.
Find the maximum possible value of after performing at most k operations optimally.
The first line contains three integers n, k and x (1 ≤ n ≤ 200 000, 1 ≤ k ≤ 10, 2 ≤ x ≤ 8).
The second line contains n integers a1, a2, ..., an (0 ≤ ai ≤ 109).
Output the maximum value of a bitwise OR of sequence elements after performing operations.
3 1 2 1 1 1
3
4 2 3 1 2 4 8
79
For the first sample, any possible choice of doing one operation will result the same three numbers 1, 1, 2 so the result is .
For the second sample if we multiply 8 by 3 two times we'll get 72. In this case the numbers will become 1, 2, 4, 72 so the OR value will be 79 and is the largest possible result.
题目链接:codeforces 578B
题目大概意思:
给定n个数字,a1,a2,a3....an(数据范围是0到10^9),然后再给定两个数字,一个数字是k,另外一个是x。接下来你可以对这n个数进行k次操作,每次操作对其中任意一个数乘以x,最后得到操作后的数组,再对这n个数进行按位或运算,题目要求的是对这n个数进行操作之后,进行按位或运算能得到的最大的数
做题的思路:首先我们知道,按位或运算的运算规则是:
解决办法:这里主要是用到了贪心的思想,然后在计算sum[i] = a[0] | a[1] | a[2] .... a[i-1] | a[i+1] | a[i+2] .... a[n-1]时,用到了优化的思想,主要是用到了预处理优化
预处理优化之——前缀和优化:
非常常见的优化方式。此处的前缀和指某数组的前i项和。
如给定数组a[n],求sum[n]。其中sum[i]=a[0]+a[1]+...+a[i]
这里的sum[]数组即为数组a的前缀和数组。
那么前缀和有什么用处呢?
假设我要求a[x]+a[x+1]+a[x+2]+...+a[y]
那么这个答案就等于 sum[y]-sum[x-1](x!=0)。若x=0则等于sum[y]。
有一点:如果原数组的数字是非负的,那么sum数组则是非递减的,在搜索的时候可以考虑使用二分。
其他预处理优化。
预处理根据题目需要求的问题,事先处理好保存起来,解决问题的时候可能需要多次使用,这样的话,只需要计算一遍,不用每次需要用的时候重复计算。
一开始的时候,我是这样求解的:
for(int j = 0;j < n;j++) for(int i = 0;i < n;i++) { if(i == j) { sum[j] = sum[j] | (a[j]*ans); continue; } else sum[j] = sum[j] | a[i]; }
这样求解之后,很快,评测出来了
答案超时了,我又读了一遍题目,发现这道题的数据量很大(200000),O(n^2)的时间复杂度肯定超时,于是我开始观察sum[i]数组的组成特点,如下所示:
sum[0] = ( ) | a[1] | a[2] | a[3] ..... | a[n-2] | a[n-1];
sum[1] = a[0]| ( ) | a[2] | a[3] ..... | a[n-2] | a[n-1];
.sum[2] = a[0]| a[1] | ( ) | a[3] ..... | a[n-2] | a[n-1];
.
.
sum[n-2] = a[0] | a[1] | a[2] | ..... | a[n-3] | ( ) | a[n-1];
sum[n-1] = a[0] | a[1] | a[2] | ..... | a[n-3] | a[n-2] | ( );
( ) 括号表示括号中的数是待操作的数字
所以呢,我就发现,在待操作的数字的两边(前缀和后缀)依次成正金字塔和倒金字塔形
sum[i+1]比sum[i]的前缀多了一个 | a[i] 而后缀少了一个 | a[i+1]
由于按位或运算时不可逆的,所以前缀可以从前往后依次加 |a[i],而后缀则从后往前
所以我就另外声明了两个数组pre[200005],suf[200005]
附上完整代码:
//时间复杂度O(n^2)超时,需要优化,已经优化成O(n) #include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; ll a[200005],pre[200005],suf[200005]; //pre,suf分别存储标记数的前缀和后缀 int main() { ll n,k,x; while(scanf("%I64d%I64d%I64d",&n,&k,&x)==3 && n && k && x) { ll ans = 1,tmp,sum = 0LL; for(int i = 0; i < k; i++) { ans = ans * x; //用来记录操作数的总积 } //printf("%I64d\n",ans); for(int i = 0; i < n; i++) scanf("%I64d",&a[i]); for(int i = 0; i < n-1; i++) pre[i + 1] = pre[i] | a[i]; //标记数的前缀 for(int i = n - 1;i > 0;i--) suf[i-1] = suf[i] | a[i]; //标记数的后缀 for(int i = 0;i < n;i++) { tmp = pre[i] | (a[i]*ans) | suf[i]; sum = max(tmp,sum); } printf("%I64d\n",sum); } return 0; }
若有错误,还请指正,谢谢。