题目大意:
思路:
首先,我们知道a or ba\ or\ ba or b肯定是不小于a和b
,也就是说,进行或运算的数字越多,结果肯定不会变小
。所以,第一问的答案就是全部数字或起来。
那么就考虑第二问。
由于进行与运算的两个数a,ba,ba,b肯定有a and b≤min(a,b)a\ and\ b\leq min(a,b)a and b≤min(a,b),所以进行与运算的数字越多,结果肯定不会变大
。所以最好就选择kkk个数。
可以把选择的数看成一个滑动窗口,那么就用num[i]num[i]num[i]表示现在窗口的数字中有多少个是二进制下第iii位为111的。
例如窗口里有333个数11,8,211,8,211,8,2,那么将这三个数分别转成二进制后就是
111111 | 888 | 222 |
---|---|---|
101110111011 | 100010001000 | 001000100010 |
所以
- num[0]=1+0+0=1num[0]=1+0+0=1num[0]=1+0+0=1
- num[1]=1+0+1=2num[1]=1+0+1=2num[1]=1+0+1=2
- num[2]=0+0+0=0num[2]=0+0+0=0num[2]=0+0+0=0
- num[3]=1+1+0=2num[3]=1+1+0=2num[3]=1+1+0=2
那么如果num[x]==mnum[x]==mnum[x]==m,说明窗口里的数字这一位全部是111,所以andandand起来就是111,否则不是。
那么就求出窗口在最左边的情况的答案,然后往右移即可。
时间复杂度:(常数为303030的)O(n)O(n)O(n),时限5s5s5s,不怂。
代码:
#include <cstdio>
#include <iostream>
#define N 1000100
#define MAXN 40
using namespace std;
int n,m,a[N],ans,num[MAXN];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
ans|=a[i];
}
printf("%d ",ans); //第一问
for (int i=1;i<=m;i++)
for (int j=0;j<=30;j++)
if ((a[i]&(1<<j))==(1<<j)) num[j]++; //窗口
ans=0;
for (int i=m+1;i<=n;i++)
{
int k=0;
for (int j=0;j<=30;j++)
{
num[j]-=((a[i-m]&(1<<j))==(1<<j));
num[j]+=((a[i]&(1<<j))==(1<<j)); //右移
if (num[j]==m) k+=(1<<j); //求答案
}
if (ans<k) ans=k;
}
printf("%d\n",ans);
return 0;
}