题目链接:点击查看
题目大意:给出 n 个数组成的序列 a ,现在可以进行的操作是,任选两个下标 i 和 j ,满足 i != j ,使得:
- 设 x = a[ i ] , y = a[ j ]
- a[ i ] = x and y
- a[ j ] = x or y
问经过任意次操作后,a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ... + a[ n ] * a[ n ] 的最大值是多少
题目分析:首先需要观察出的一个前置性质就是,x + y = ( x or y ) + ( x and y ),这个对于 x 与 y 的其中一位打个表就能看出来
x | 0 | 0 | 1 | 1 |
---|---|---|---|---|
y | 0 | 1 | 0 | 1 |
x&y | 0 | 0 | 0 | 1 |
x|y | 0 | 1 | 1 | 1 |
x+y | 0 | 1 | 1 | 2 |
(x&y)+(x|y) | 0 | 1 | 1 | 2 |
这个性质,换句话说,就是无论如何变化,序列 a 中每一位上的 “ 1 ” 的个数是固定的
这样一来我们就可以贪心去处理了,如果想让平方和最大的话,那么显然让每一个数尽量大是最优的,这样我们可以先贪心将第一个数的每一位全部赋值为 1 ,然后再这样贪心处理第二个、第三个等等
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
int cnt[30];
void solve(int num)
{
for(int i=0;i<30;i++)
cnt[i]+=((num>>i)&1);
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int num;
scanf("%d",&num);
solve(num);
}
LL ans=0;
for(int i=1;i<=n;i++)
{
LL num=0;
for(int j=0;j<30;j++)
{
if(cnt[j])
{
cnt[j]--;
num|=(1<<j);
}
}
ans+=num*num;
}
printf("%lld\n",ans);
return 0;
}