Problem
Problem Statement
You are given an integer sequence of length N , A = ( A 1 , A 2 , ⋯ , A N ) N, A = (A_1, A_2, \cdots, A_N) N,A=(A1,A2,⋯,AN). Snuke wants to make A A A a non-decreasing sequence.
He can perform the following operation zero or more times.
- Choose two adjacent elements of A A A
- Remove these two elements and insert the bitwise OR \text{OR} OR of these two values at the original position
Find the maximum possible value of the length of A A A when A A A becomes a non-decreasing sequence.
Constraints
- 1 ≤ N ≤ 2 × 10 5 1 \le N \le 2 \times 10^5 1≤N≤2×105
- 1 ≤ A i < 2 30 1 \le A_i < 2^{30} 1≤Ai<230
- All input values are integers.
Translation
问题描述
给定一个长度为 N N N 的整数序列 A = ( A 1 , A 2 , ⋯ , A N ) A = (A_1, A_2, \cdots, A_N) A=(A1,A2,⋯,AN)。Snuke 想要将 A A A 变成一个非递减序列。
他可以执行以下操作零次或多次:
- 选择 A A A 中两个相邻的元素
- 移除这两个元素,并在原位置插入这两个值的按位或( OR \text{OR} OR)结果
请找出当 A A A 成为非递减序列时,序列 A A A 的最大可能长度。
限制条件
- 1 ≤ N ≤ 2 × 10 5 1 \le N \le 2 \times 10^5 1≤N≤2×105
- 1 ≤ A i < 2 30 1 \le A_i < 2^{30} 1≤Ai<230
- 所有输入值均为整数。
Solutions
Approach 1
动态规划,RMQ,动态规划的状态设计优化。
首先,用 RMQ 维护区间按位或的结果,通过预处理 log 2 ( n ) \log_2(n) log2(n) 的结果可以实现 O ( 1 ) O(1) O(1) 查询区间按位或。
先考虑最基础的可解 dp 方程,不难设计 d p i j = k dp_{ij} = k dpij=k 代表当前处理完了区间 1 ∼ i 1\sim i 1∼i ,且最后一个数的值为 j j j 下的最大序列长度,这是一个可解的 dp 方程,复杂度主要来源于枚举 j j j 的 O ( A i ) O(A_i) O(Ai) ,而枚举 i i i 的 O ( n ) O(n) O(n) 和转移的 O ( 1 ) O(1) O(1) 是无法省略的,所以尝试能否降低枚举 j j j 的复杂度。
显然,以 i i i 为结尾,向前按位或的可能情况最多有 i i i 种,复杂度降了,但还是不够。
进一步结合按位或的性质,可以发现,首先,向前的区间按位或值是单调不降的,且每一个二进制位变为 1 后就不会再变为 0 ,而什么时候不变,就是当当前按位或上来的数是区间按位或的子掩码时。那么显然,最多的变化次数就是 ⌈ log 2 ( max ( a 1 ∼ a i ) ) ⌉ \lceil \log_2(\max(a_1 \sim a_i))\rceil ⌈log2(max(a1∼ai))⌉ 次,这不会大于 30 。于是可以预处理每一个位置向前所有的按位或和。
现在枚举 j j j 的复杂度降了,只是转移时需要额外查找对应的按位或和是第几个,但复杂度依旧满足。
Code
cpp
#include<bits/stdc++.h>
using namespace std;
std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 998244353;
const double ept = 1e-9;
ll add(ll x, const ll&y) { x += y; if(x >= mod) { x -= mod; } return x; }
ll sub(ll x, const ll&y) { x -= y; if(x < 0) { x += mod; } return x; }
ll mul(ll x, const ll&y) { x = x * y; if(x >= mod) { x %= mod; } return x; }
#ifndef ONLINE_JUDGE
#define DEBUG 1
#define fastio
#define coutd cout
#define db(i) cout << #i << ": " << i << endl;
#define dbl(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__);
#define dbi(vec) { cout << #vec << ": "; for(auto &v: vec) { cout << v << ' '; } cout << endl; }
#define dbv(vec, a, b) { cout << #vec << ": "; for(int i=a; i!=b+sig(b-a); i+=sig(b-a)) { cout << vec[i] << ' '; } cout << endl; }
#else
#define DEBUG 0
#define fastio ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define coutd cerr
#define db(...)
#define dbl(fmt, ...)
#define dbi(vec)
#define dbv(vec, a, b)
#endif
int n;
int a[200200];
vector<int> b[200200];
int f[30][200200];
int dp[200200][100];
int lg2[200200];
void solve(int T) {
cin >> n;
for(int i=1; i<=n; i++) cin >> a[i];
bool flag;
for(int i=1; i<=n; i++) {
b[i].push_back(a[i]);
for(const auto &u: b[i-1]) {
auto p = a[i] | u;
flag = false;
for(const auto &v: b[i]) if(v == p) { flag = true; break; }
if(flag) continue;
b[i].push_back(p);
}
}
for(int j=1; j<=n; j++) f[0][j] = a[j];
for(int i=1; i<30; i++)
for(int j=1; j<=n; j++)
f[i][j] = f[i-1][j] | f[i-1][min(j+(1<<(i-1)), n)];
int ans = 1;
dp[1][0] = 1;
for(int i=1; i<n; i++) {
for(int j=0; j<b[i].size(); j++) {
if(dp[i][j] == 0) continue;
int u = b[i][j] | a[i+1];
for(int k=0; k<b[i+1].size(); k++)
if(u == b[i+1][k]) {
dp[i+1][k] = max(dp[i+1][k], dp[i][j]);
break;
}
int l = i+1, r = n+1, mid, p;
while(l < r) {
mid = l + (r-l) / 2;
p = lg2[mid-(i+1)+1];
if((f[p][i+1]|f[p][mid-(1<<p)+1]) >= b[i][j]) r = mid;
else l = mid + 1;
}
if(l > n) break;
p = lg2[l-(i+1)+1];
u = f[p][i+1] | f[p][l-(1<<p)+1];
// cout << i << ' ' << l << ' ' << p << ' ' << u << endl;
for(int k=0; k<b[l].size(); k++)
if(u == b[l][k]) {
dp[l][k] = max(dp[l][k], dp[i][j] + 1);
ans = max(ans, dp[l][k]);
break;
}
}
}
cout << ans << endl;
}
void init() {
for(int i=2; i<=2e5; i++) lg2[i] = lg2[i/2] + 1;
}
signed main() {
//freopen("1.in", "r", stdin);
//freopen("1.out", "w", stdout);
//cout.flags(ios::fixed); cout.precision(8);
fastio
init();
int T_=1;
// std::cin >> T_;
for(int _T=1; _T<=T_; _T++) { solve(_T); }
return 0;
}