思路:将所有数全部插入堆中,然后关注最小的几个数,容易发现两个数进行按位与之后这些数的和会减小这两个数按位或的结果,于是在这几个数中找到按位或结果最小的两个数进行按位与并且插入堆中即可。
注意第 25 行及后面的代码,由于不知道每次应该找最小的几个数进行操作,一开始这里数组 ta
的大小(即每次操作关注的数量)开到 3、4、5 的时候都出现了 WA 的情况,但是后面开到 7 就过了。
感觉这似乎是个贪心的乱搞做法(最慢的点跑了 900+ms)但是过了,因此来求 Hack/证明。
(警示后人:在将数组大小改成 5 后想了半天的正解,然后过了一个小时突然想把数组大小再改大点就过了,所以 IOI 赛制一定要大胆提交否则容易后悔的)
赛时代码:
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
constexpr int N=2e6;
priority_queue<int,vector<int>,greater<int> >a;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
a.push(t);
}
for(int p=1;p<=n-k;p++)
{
int ta[7],cnt,mni=1,mnj=0;
for(cnt=0;cnt<7;cnt++)
{
if(a.empty())
break;
ta[cnt]=a.top();
a.pop();
}
for(int i=2;i<cnt;i++)
{
for(int j=0;j<i;j++)
{
if((ta[mni]|ta[mnj])>(ta[i]|ta[j]))
{
mni=i;
mnj=j;
}
}
}
for(int i=0;i<cnt;i++)
{
if(i!=mni&&i!=mnj)
a.push(ta[i]);
}
a.push(ta[mni]&ta[mnj]);
}
long long ans=0;
while(a.size())
{
ans+=a.top();
a.pop();
}
printf("%lld\n",ans);
}
return 0;
}