[正睿集训2021] 杂题再讲
Posted C202044zxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[正睿集训2021] 杂题再讲相关的知识,希望对你有一定的参考价值。
你没看错就是 \\(2021\\) 年的题,他就是诈尸了。
Bank Security Unification
题目描述
给定长度为 \\(n\\) 的数列,希望您从中选出一个子序列,使得相邻两项按位与之和最大。
\\(2\\leq n\\leq 10^6,a_i\\leq 10^12\\)
解法
首先不难想到一个 \\(dp\\),设 \\(dp[i]\\) 表示考虑前 \\(i\\) 个数,子序列的结尾是 \\(a_i\\) 的最大权值。
考虑这样一个 \\(\\tt observation\\):如果 \\(a_j\\) 和 \\(a_k\\) 的最高位相同,并且 \\(j<k\\),那么从 \\(k\\) 转移一定比从 \\(j\\) 转移更优,这是因为 \\(a_i\\and a_k+a_k\\and a_j\\geq a_i\\and a_j\\)
这说明了对于每个数位我们只需要保留离 \\(i\\) 最近的一个数,把他们存下来暴力转移时间复杂度 \\(O(n\\log n)\\)
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 1000005;
#define int long long
int read()
int x=0,f=1;char c;
while((c=getchar())<\'0\' || c>\'9\') if(c==\'-\') f=-1;
while(c>=\'0\' && c<=\'9\') x=(x<<3)+(x<<1)+(c^48);c=getchar();
return x*f;
int n,a[M],p[M],dp[M];
signed main()
n=read();
for(int i=1;i<=n;i++)
a[i]=read();dp[i]=dp[i-1];
for(int j=0;j<40;j++) if(p[j])
dp[i]=max(dp[i],dp[p[j]]+(a[i]&a[p[j]]));
for(int j=0;j<40;j++)
if(a[i]&(1ll<<j)) p[j]=i;
printf("%lld\\n",dp[n]);
以上是关于[正睿集训2021] 杂题再讲的主要内容,如果未能解决你的问题,请参考以下文章