其他分享
首页 > 其他分享> > CF1368D题解

CF1368D题解

作者:互联网

原题

CF1368D AND, OR and square sum


思路概述

题意分析

给定一个长度为 \(n\) 的数列,对于其中两个数 \(a_i,a_j(1≤i,j≤n)\) 每次可以执行操作 \(a_i\text{ AND }a_j→a_i,a_i\text{ OR }a_j→a_j\) ,求不限次数的操作后能得到的最大数列平方和。

思路分析

首先对这两种运算方式进行分析。与运算等于对二进制下的两数求交集,或运算则等于对二进制下的两数求并集。根据简单的数学常识不难得出 \((a_i\text{ OR }a_j)+(a_i\text{ AND }a_j)=a_i+a_j\) ,也就是说每次操作并不改变两数的和。

一个常识:两个和为定值的正整数,差越大,平方和越大(篇幅原因,笔者不在此处证明)。

由上述内容就可以知道,对于本题的要求,只需要记录所有数各位上 \(1\) 的数量,再尽量在数列两端分别构造最大最小的数即得到答案。


算法实现

关于最大(最小)数的构造

首先对输入的每个数进行二进制拆分和记录,可以得到二进制形式下 \(0\) 到 \(19\) 位的 \(1\) 的数量。

对于最大数,尽量将所有位能加入的 \(1\) 加上。构造完最大数,剩下的数自然是最小数。

注意事项

由于求平方和,而构造出的数 \(a_i\) 处于区间 \([0,2^{20})\) ,所以在 int 范围内可能会溢出,需要开 long long 类型变量。


AC code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<ctime>
#define ll long long
#define RL register long long
using namespace std;
const ll maxs=30;
ll n,ans;
ll rec[maxs];
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin >> n;
	for(RL i=1,inp;i<=n;++i)
	{
		cin >> inp;
		for(RL j=0;j<20;++j) 
			rec[j]+=((inp>>j)&1);
	}
	for(RL i=1,temp=0;i<=n;++i,temp=0)
	{
		for(RL j=0;j<20;++j) 
			if(rec[j])
			{
				temp+=1<<j;
				--rec[j];
			}
		ans+=temp*temp;
	}
	cout << ans;	
	return 0;
}

标签:二进制,题解,ll,long,CF1368D,text,RL,include
来源: https://www.cnblogs.com/frkblog/p/16365372.html