Luogu P4310 绝世好题

题目大意

%   给定 n n n 个数组成的序列 A = { a i } A=\{a_i\} A={ai},定义一个长度为 N N N 的序列 B = { b i } B=\{b_i\} B={bi} 合法当且仅当 ∀ i ∈ [ 1 , N ] ∩ Z , b i  and  b i − 1 ≠ 0 \forall i\in[1,N]∩\Z,b_i\ \text{and}\ b_{i-1}\not=0 i[1,N]Z,bi and bi1=0,求 A A A 中最长的合法子序列长度。
%   数据范围  1 ⩽ n ⩽ 1 0 5 , 1 ⩽ a i ⩽ 1 0 9 1\leqslant n\leqslant 10^5,1\leqslant a_i\leqslant 10^9 1n105,1ai109

题解

%   考虑动态规划,定义 f [ i ] f[i] f[i] 表示以第 i i i 个数为结尾的合法子序列长度,转移如下 f [ i ] = max ⁡ j ∈ [ 1 , i )   ,   a i  and  a j ≠ 0 f [ j ] + 1 f[i]=\max_{j\in[1,i)\ ,\ a_i\ \text{and}\ a_j\not=0}f[j]+1 f[i]=j[1,i) , ai and aj=0maxf[j]+1

%   这样做的时间复杂度为 Θ ( n 2 ) \Theta(n^2) Θ(n2),空间复杂度为 Θ ( n ) \Theta(n) Θ(n)

#include<cstdio>
#define max(a,b) (a>b?a:b)
#define maxn 100010
int a[maxn],f[maxn],n;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	register int ans=0;
	for(register int i=1;i<=n;i++){
		f[i]=1;
		for(register int j=1;j<i;j++)
			if(a[j]&a[i])
				f[i]=max(f[j]+1,f[i]);
		ans=max(ans,f[i]);
	} printf("%d\n",ans);
	return 0;
}

%   考虑更换定义 f [ i ] [ k ] f[i][k] f[i][k] 表示前 i i i 个数能选出的子序列中,子序列最后一个数的二进制下第 j j j 位(从低位(第0位开始),向高位编号)为 1 1 1 的最长长度。状态转移如下:
f [ i ] [ j ] = max ⁡ { f [ i − 1 ] [ j ] max ⁡ a i  and  2 k = 1 f [ i − 1 ] [ k ] + 1 , a i  and  2 j = 1 f[i][j]=\max\begin{cases}f[i-1][j]\\\\ \begin{aligned}\max_{a_i\ \text{and}\ 2^k=1}f[i-1][k]+1\end{aligned}&,a_i\ \text{and}\ 2^j=1\end{cases} f[i][j]=max f[i1][j]ai and 2k=1maxf[i1][k]+1,ai and 2j=1

%   这样朴素转移时间复杂度为 Θ ( n log ⁡ 2 2 a i ) \Theta(n\log_2^2 a_i) Θ(nlog22ai),空间复杂度可以降到 Θ ( log ⁡ 2 2 a i ) \Theta(\log_2^2 a_i) Θ(log22ai)(降维)

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000010
int n,f[maxn][32];
int main(){
	int ans=0;
	scanf("%d",&n);
	for(int i=1,a;i<=n;i++){
		scanf("%d",&a);
		for(int j=0;j<=30;j++){
			f[i][j]=f[i-1][j];
			if(!(a&(1<<j))) continue;
			for(int k=0;k<=30;k++){
				if(a&(1<<k))
					f[i][j]=max(f[i][j],f[i-1][k]+1);
			}
			ans=max(ans,f[i][j]);
		}
	} printf("%d\n",ans);
	return 0;
}

%   考虑到转移的时间瓶颈在于需要枚举上一个数和这一个数在那一位上相同,而这个过程与 j j j 无关,因此我们可以处理出这部分的答案,再进行转移。时间复杂度 Θ ( n log ⁡ 2 a i ) \Theta(n\log_2 a_i) Θ(nlog2ai)

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000010
int n,f[maxn][32];
int main(){
	int ans=0;
	scanf("%d",&n);
	for(int i=1,a;i<=n;i++){
		scanf("%d",&a);
		int tran=0;
		for(int k=0;k<=30;k++)
			if(a&(1<<k)) tran=max(tran,f[i-1][k]+1);
		for(int j=0;j<=30;j++){
			if(a&(1<<j)) f[i][j]=max(f[i][j],tran);
			else f[i][j]=f[i-1][j];
			ans=max(ans,f[i][j]);
		}
	} printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值