哭死,比赛的时候完全想歪了,想的是考虑一次合并能造成多大的贡献,按照贡献排序然后合并。这样做只能考虑局部造成的贡献,然而最后算的时候要考虑整体,所以并不是很对。
正着想没有思路就可以倒着想,考虑枚举答案。
合并k次,意味着最后是n-k个数。
经典从二进制高位到低位考虑,考虑这一位(假设为第i位)能否出现在答案里?那我们就让原序列最后合并完后的数每个数第i位都是1,在此要求下让合并完的数尽可能的多(只要最后留下的数的个数大于等于n-k就可以,然后我们就能接着考虑二进制下一位)。怎们考虑让合并完的数尽可能的多?我们设nt[now][j]表示第now个数及以后最早出现“二进制第j位为1”的位置在哪,就很好让最后合并完的数尽可能多了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,ans;
const int N=200010;
int a[N],nt[N][32],las[233];
int pan(int c)
{
int cnt=0,now=1;
while(now<=n)
{
int maxx=0;
for(int j=1;j<=31;++j)
if((c>>(j-1))&1)
{
maxx=max(maxx,nt[now][j]);
}
//cout<<" -> "<<now<<" "<<maxx<<endl;
if(maxx<=n)++cnt,now=maxx+1;
else break;
}
return cnt>=k;
}
signed main()
{
cin>>n>>k;k=n-k;
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
for(int i=1;i<=31;++i)las[i]=n+1;
for(int i=n;i>=1;--i)
{
for(int j=1;j<=31;++j)
if((a[i]>>(j-1))&1)las[j]=i;
for(int j=1;j<=31;++j)nt[i][j]=las[j];//,cout<<"-- "<<i<<" "<<j<<" "<<nt[i][j]<<endl;
}
for(int i=31;i>=1;--i)
{
ans|=(1ll<<(i-1));
if(pan(ans)==0)ans^=(1ll<<(i-1));
}
cout<<ans;
return 0;
}
/*
5 2
2 1 2 3 1
20 7
46734244 7155744 419668125 71371568 686112 90931877 235739656 174560001 73941537 157614741 557848156 172496544 449681808 258216481 628704657 317530505 436971280 329908401 168398248 163412001
4194304
*/
标签:洛谷,int,P10512,合并,二进制,序列,考虑,now,nt
From: https://www.cnblogs.com/wljss/p/18201431