CodeForces - 1368D AND, OR and square sum(位运算+贪心)

题目链接:点击查看

题目大意:给出 n 个数组成的序列 a ,现在可以进行的操作是,任选两个下标 i 和 j ,满足 i != j ,使得:

  1. 设 x = a[ i ] , y = a[ j ]
  2. a[ i ] = x and y
  3. 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;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/106860638