【SSLOJ 3348】位運算

SSL_LMZ發表於2024-11-16

題目大意

給定 \(n\)個非負整數,每次你可以選擇兩個數\(a,b\) ,將其中一個數變為 \(a\ and\ b\),另一個變為 \(a\ or\ b\),你可以進行多次操作,任何時候都可以停止,請最大化所有數的平方和。

輸入格式
第一行包含一個正整數 \(n\)

第二行包含 \(n\)個用空格分開的非負整數 \(a_i\)

輸出格式
一行一個非負整數表示最後最大化的所有數的平方和。

【輸入樣例】

5
1 2 3 4 5

【輸出樣例】

99

【樣例解釋】

一組最優方案是變成 \(7,0,7,0,1\),答案是 \(99\)

對於\(100\%\)的資料,\(N\le10^5,a_i\le10^6\)

基本思路

位運算當然要在二進位制下考慮啦。我們先隨便拉兩個數出來\((101010)_2\)\((110101)_2\) 最後操作完就是 \((111111)_2\)\((100000)_2\) 那麼我們就發現了這個操作就是將一個數所有可以挪過去的 \(1\) 挪過去。

上過初中的都知道,\(a^2+b^2\le (a+b)^2\),所以我們把所有數摞起來,把所有 \(1\) 儘量往一個數上推就可以了。

核心程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,a,cnt[30];
ll ans;
int main(){
	freopen("andor.in","r",stdin);
	freopen("andor.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a;
		for(int j=0;j<=25;j++){
			if(a&(1<<j))
				cnt[j]++;
		}
	}
	for(int i=1;i<=n;i++){
		a=0;
		for(int j=0;j<=25;j++){
			if(cnt[j]>0){
				cnt[j]--;
				a|=(1<<j);
//				cout<<j<<":"<<a<<endl;
			}
		}
		ans+=1ll*a*a;
	}
	cout<<ans;
	return 0;
}