Codeforces Round #815 (Div. 2) D

D

题意

给定长度为n的序列a,需要你寻找一个最长子序列b,满足bi ^ (i+1) < bi+1 ^ i
输出b的最大长度

思路

首先将给的那个异或条件变形,但是异或性质太少了,实在是没办法直接变形,
但是根据直觉,如果是等于号 我们可以化简为 bi ^ i == b i+1 ^ (i + 1)(p式)
我们考虑二进制形式前k位等式成立,而k+1位使得bi ^ (i+1) < bi+1 ^ i
即 在第k+1位上 bi+1 ^ i = 1,而bi ^ (i+1) = 0
通过枚举发现,这时候k+1位 bi ^ i != bi+1 ^ (i+1) ,我们找到了一个性质,当原不等式为等号时,p式成立,当枚举二进制位第一个不成立式,p式也不成立
所以我们构建01Trie字典树来查询小于bi ^ i 的每一位数来实现最长上升子序列dp的状态转移,因为是异或,有bi本身第k位0 或 1的对应,我们还要存储Trie每个节点的位置是0或1的最长上升子序列个数

代码实现

const int N = 32 * (3e5 + 10), M = 3e5 + 10;
int tr[N][2], f[N][2];
int n, dp[M], a[M];
int cnt;
void insert (int x, int idx)
{
	int u = 0;
	
	for (int i = 30 ; i >= 0; i --)
	{
		int p = (x >> i) & 1;
		
		if (!tr[u][p]) tr[u][p] = ++ cnt;
		
		u = tr[u][p];
		f[u][ (idx >> i) & 1] = max (f[u][ (idx >> i) & 1], dp[idx]);
	}
}
int query (int x, int k)
{
	int res = 1;
	int u = 0;
	
	for (int i = 30 ; i >= 0 ; i --)
	{
		int p = (x >> i) & 1;
		// q 是 b[i] ^ 1 的节点编号
		int q = tr[u][p ^ 1];
		// 根据第k位 b[i] ^ 1来更新res
		res = max (res, f[q][ (k >> i) & 1 ^ 1] + 1);
		
		if (!tr[u][p]) break;
		
		u = tr[u][p];
	}
	
	return res;
}
void solve()
{
	cin >> n;
	// 初始化
	for(int i = 0; i <= cnt ; i ++) tr[i][0] = tr[i][1] = f[i][0] = f[i][1] = 0;
	cnt = 0;
	
	for (int i = 0 ; i < n ; i ++)
		cin >> a[i];
		
	for (int i = 0; i < n ; i ++)
	{
		dp[i] = query (a[i] ^ i, a[i]);
		insert (a[i] ^ i, i);
	}
	
	int res = 0;
	
	for (int i = 0; i < n ; i ++)
		res = max (res, dp[i]);
		
	cout << res << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值