牛客練習賽45----D-Data Structure

首先發出題目鏈接:
鏈接:https://ac.nowcoder.com/acm/contest/847/D
來源:牛客網
涉及:按位運算


題目如下:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
說實話,按位運算挺重要的,快速冪有了按位運算才降低了複雜度,有和無本身就是對立的,有了0和1所代表的按位運算,才使得我們能夠從一個數的結構上分析或者改變數字


我們先來說如何初步地得到一個最大的k–or–and的值

要讓k-or-and值最大,首先是先讓分段後每一段的or值最大,那麼and後的值當然就是最大的,要想找到這個每一段中or的最大值,可以通過枚舉的方式,由於這個最大or數的二進制位最大隻有30位,可以從二進制位的最高位開始枚舉每一段or的最大值。

舉個例子
我們假設第30位是1,就得到100…000(共29個0),然後貪心的驗證,存不存在k段,使得每一段的or值的二進制位第30位爲1,如果存在,那麼最大or值二進制位的第30位一定是1,否則就是0.

然後,假如第30位是1,再來假設第29位也是1,就得到1100…000,在來貪心的驗證能不能將序列分成k段,使得每一段的or值的二進制位第30位爲1且二進制第29位爲1,如果存在,那麼最大or值二進制位的第29位也是1,否則就是0.

再然後,假設第29位不能爲1,那麼再假設第28位是1,就得到10100…0000,在來貪心的驗證能不能將序列分成k段,使得每一段的or值的二進制位第30位爲1,二進制第29位爲0,二進制第28位爲1(以此類推)…

問題:如何來貪心得進行驗證?
加入當前得到的數是101000…000(ans),從序列第一個數開始往後一直或,直到或到一個數,使得這一系列的數或之後的值(cnt)與ans的與值等於ans(表示ans的二進制位爲1,cnt在這個位置也是1,即**(cnt&ans)==ans**),於是剛剛或的一系列的數即爲這個序列的一段,然後這樣再繼續找下一段,滿足了每一段的or值都是ans,然後看一看是否分成有k段,如果有,表示驗證成功,否則驗證失敗。

按照以上的方法,判斷完30位的每一位,就得到了一個數,這個數就是最大的k-or-and值

bool check(ll ans){
	ll cnt=0,ant=0;//cnt是當前一系列的或值,ant代表當前已分段數
	for(int i=1;i<=n;i++){
		cnt|=num[i];
		if((cnt&ans)==ans)	ant++,cnt=0;//滿足條件,分段數加一,cnt清零,從新開始新一段的或
	}
	//再判斷是否已經分了至少k段
	if(ant>=k)	return true;
	else	return false;
}

下面來說一說如何修改

不多說,直接把序列中每一個值進行修改,再套用上面求k-or-and最大值的方法肯定超市。

所以得找技巧!

根據或和與的確定性可知:
1.當序列所有的數或上一個x,當x二進制第i位是1,那麼序列中所有數二進制的第i位都會變成1,最後k-or-and值得二進制位的第i位就鎖定爲1;
2.當序列所有的數與上一個x,當x二進制第i位是0,那麼序列中所有數二進制的第i位都會變成0,最後k-or-and值得二進制位的第i位就鎖定爲0;

但是,不一定最後k-or-and值二進制每一位的數都發生變化,也就是說,只有那些被鎖定的值是確定的,沒有被鎖定的值仍然是未知的

ps:每一次或上或者與上一個數,可能出現新的位置被鎖定了,也有可能沒有出現新的被鎖定的位置。


於是,求最大k-or-and的方法就會發生變化

不需要再一個個判斷所有的30和位置,我們只用討論那些沒有被鎖定的位置

最後的k-or-and的最大值就是沒有鎖定位置的值加上鎖定位置的值

但是注意,在這裏需要剪枝
如果上一次或上或者與上一個值,沒有出現新的鎖定的位置,那麼,我們可以跳過判斷沒有鎖定位置值得這一步驟!可以用一個數組來判斷每一個位置是否被鎖定,然後用一個bool類型標記,來判斷是否出現新的鎖定位置


舉個例子:

比如題目例子(這裏只討論求k-or-and值,由於一開始沒有任何位置被鎖定,所以要全部位都要討論
n=3,k=2,序列爲{11,30,4}

序列 二進制數
11 1011
30 11110
4 100

可以從10000開始討論:
1.當ans=10000時

序列 二進制數 滿足條件分段後的or值
11 1011
30 11110 11,30爲一段,or值爲11111
4 100

只能分成一段{11,30,4},不滿足條件,故ans第五位爲0

2.當ans=1000時

序列 二進制數 滿足條件分段後的or值
11 1011 11爲一段,or值爲1011
30 11110 30爲一段,or值爲11110
4 100

可以分成兩段{11}{30,4},滿足條件,故ans第四位爲1

3.當ans=1100時

序列 二進制數 滿足條件分段後的or值
11 1011
30 11110 11,30爲一段,or值爲11111
4 100

只能分成一段{11,30,4},不滿足條件,故ans第三位爲0

4.當ans=1010時

序列 二進制數 滿足條件分段後的or值
11 1011 11爲一段,or值爲1011
30 11110 30爲一段,or值爲11110
4 100

可以分成兩段{11}{30,4},滿足條件,故ans第二位爲1

5.當ans=1011時

序列 二進制數 滿足條件分段後的or值
11 1011 11爲一段,or值爲1011
30 11110
4 100

只能分成一段{11,30,4},不滿足條件,故ans第一位爲0

所以最大的k-or-and值爲1010(10)。


代碼如下:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#define INF 200005
using namespace std;
typedef long long ll;
int n,k,q;//題目定義的變量
int flag;//flag存題目定義的操作類型數
ll num[INF],x;//num數組存題目中的A數組
int bit[32];//bit數組存被鎖定的位置,以及鎖定位置對應的鎖定值,當bit[I]=-1表示這一位未被鎖定
bool updata=true;//updata判斷於或者或之後是否出現新的鎖定位
bool check(ll ans){
	ll cnt=0,ant=0;//ant存當前已經分段數,ant存當前一段的or值
	for(int i=1;i<=n;i++){//貪心的判斷能不能分成k段
		cnt|=num[i];
		if((cnt&ans)==ans)	ant++,cnt=0;//能,則重新開始分段
	}
	if(ant>=k)	return true;//能貪心分成k段
	else	return false;
}
int main(){
	cin>>n>>k;
	ll ans;//在查詢最大K-OR-AND事使用,用來貪心枚舉未鎖定位置的的最大分段or值
	memset(bit,-1,sizeof(bit));//把bit數組初始化爲-1,表示所有位置都沒有被鎖定
	int i;
	for(i=1;i<=n;i++)	cin>>num[i];
	cin>>q;
	while(q--){
		cin>>flag;
		if(flag==3){
			ll final=0;//final存所有被鎖定位置中,鎖定值爲1的貢獻
			for(i=0;i<=30;i++)
				if(bit[i]==1)	final+=(1<<i);
			if(updata){//如果沒有出現新的所定值,就不用再次對未鎖定的位置進行貪心
				ans=0;//初始化爲0
				updata=false;
				for(i=30;i>=0;i--){
					if(bit[i]!=-1)	continue;//跳過鎖定位置
					ans^=1<<i;//假設二進制這一位爲1
					if(!check(ans))	ans^=1<<i;//如果不能爲1,則爲0
				}
			}
			cout<<final+ans<<endl;//鎖定位置的值加未鎖定位置的值就是答案
		}
		else if(flag==2){//與
			cin>>x;
			for(i=0;i<=30;i++)
				if(!(x&1<<i)){//判斷x的二進制的第i位是不是爲0
					if(bit[i]==-1)	updata=true;//如果是0,且k-or-and這一位未被鎖定,則最大k-or-and這一位鎖定
					bit[i]=0;//鎖定爲0
				}
		}
		else{//或
			cin>>x;
			for(i=0;i<=30;i++)
				if(x&1<<i){//判斷x的二進制的第i位是不是爲1
					if(bit[i]==-1)	updata=true;//如果是1,且k-or-and這一位未被鎖定,則最大k-or-and這一位鎖定
					bit[i]=1;//鎖定爲1
				}
		}
	}
	return 0;
}
發佈了49 篇原創文章 · 獲贊 91 · 訪問量 3987
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章