洛谷 P10512 序列合并

哭死,比赛的时候完全想歪了,想的是考虑一次合并能造成多大的贡献,按照贡献排序然后合并。这样做只能考虑局部造成的贡献,然而最后算的时候要考虑整体,所以并不是很对。

正着想没有思路就可以倒着想,考虑枚举答案。

合并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
*/
posted @ 2024-05-20 10:53  wljss  阅读(6)  评论(0编辑  收藏  举报