标签:二进制 题解 ll long CF1368D text RL include
原题
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
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。