1. 程式人生 > >序列【模擬】

序列【模擬】

題目大意:

在這裡插入圖片描述


思路:

首先,我們知道 a   o r   b a\ or\ b

r b肯定是不小於a和b,也就是說,進行或運算的數字越多,結果肯定不會變小。所以,第一問的答案就是全部數字或起來。
那麼就考慮第二問。
由於進行與運算的兩個數 a , b a,b 肯定有 a  
a n d   b m i n ( a
, b ) a\ and\ b\leq min(a,b)
,所以進行與運算的數字越多,結果肯定不會變大。所以最好就選擇 k k 個數。
可以把選擇的數看成一個滑動視窗,那麼就用 n u m [ i ] num[i] 表示現在視窗的數字中有多少個是二進位制下第 i i 位為 1 1 的。
例如窗口裡有 3 3 個數 11 , 8 , 2 11,8,2 ,那麼將這三個數分別轉成二進位制後就是

11 11 8 8 2 2
1011 1011 1000 1000 0010 0010

所以

  • n u m [ 0 ] = 1 + 0 + 0 = 1 num[0]=1+0+0=1
  • n u m [ 1 ] = 1 + 0 + 1 = 2 num[1]=1+0+1=2
  • n u m [ 2 ] = 0 + 0 + 0 = 0 num[2]=0+0+0=0
  • n u m [ 3 ] = 1 + 1 + 0 = 2 num[3]=1+1+0=2

那麼如果 n u m [ x ] = = m num[x]==m ,說明窗口裡的數字這一位全部是 1 1 ,所以 a n d and 起來就是 1 1 ,否則不是。
那麼就求出視窗在最左邊的情況的答案,然後往右移即可。
時間複雜度:(常數為 30 30 的) O ( n ) O(n) ,時限 5 s 5s ,不慫。


程式碼:

#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;
}