【DP,位运算】 Bank Security Unification

传送门:https://codeforces.com/gym/102956/problem/D

题目大意:给出一个数列 a ,选出一个子序列 ak ,使得子序列(长度记为 leni=1len1aki&aki+1 最大。

分析

我们记 f[i][j] 为处理到第 i 位,长度为j的子序列的最大值

若第i位不选 f[i][j]=f[i1][j]
否则 f[i][j]=max(f[pre][j1]) 其中 pre 表示 i 之前所有可能的下标。

采取滚动数组的思想,我们可以优化一维,进而得到:
f[i]=max(f[i1],f[pre])

但是直接枚举 pre 需要 o(N) 复杂度,显然超时,我们从位运算的角度考虑优化:

我们记 highbit(x)是我乱起的)为二进制中 x 除了最高位为 1 其余全部清 0 所对应的数,例如:highbit(11001)=10000

那么我们有这样一个结论:对于选出的所求值最大的子序列最后的两个数 ai,aj 不存在 att(i,j)),使得highbit(ai&aj)=highbit(ai&at)。(反证法易得)

这意味着 f[i] 只可能由 f[pre[j]] 转移而来,这里 pre[j] 表示离 i最近的,对应二进制位置 j1 的数所对应的下标。(结合代码理解)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e6+5;
ll f[N], pre[N];
int n;
ll a[N];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	
	for(int i=1;i<=n;i++){
		f[i]=f[i-1];
		for(int j=0;j<=40;j++){
			if(a[i]>>j&1){
				f[i]=max(f[i],f[pre[j]]+(a[pre[j]]&a[i]));
				pre[j]=i;
			}
		}
	}
	
	cout<<f[n]<<endl;
	
	return 0;
}
posted @   HinanawiTenshi  阅读(124)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示