Codeforces Round 781 (Div. 2) E. MinimizOR (可持久化字典树)
题目大意:
T组测试数据每组测试数据先输入一个n表示有一个长度为n的一维数组,然后输入n个数字表示这个一维数组。紧接着输入一个k表示有k个询问,对于每个询问会输入一个l和一个r表示询问数组中[l, r]这个区间里面任意两个下标不重复的元素最小的或(|)是多少。
可持久化trie版本
我们可以贪心的去考虑,如果高位为0一定是最优的。考虑可持久化字典树贪心,从高位往低位尽心贪心,如果[l, r]这个区间里面第x位的代表0的这颗子树的大小大于等于2就可以直接往左走最优,若不是的话我们选择直接往右走,此时我们左子树大小最大为1,若为1我们用两个数组rx和ry记录root[l - 1]和root[r]这两个版本此时往左走的编号。因为每走一步最多有一个分支,所以一次查询的时间复杂度最坏为30 * 30。可以通过此题。
#include <bits/stdc++.h>
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
using ll = long long;
typedef std::pair<int, int> PII;
using namespace std;
int n, m;
const int N = 1e5 + 10, M = 3e5 + 10;
struct Persistent_Tire{
int ch[M * 31][2], ver[M * 31], root[M * 31], idx, sz[M * 31];
int sigma;
int rx[N], ry[N];
inline void Init(){
ver[0] = -1;//边界
idx = 0;
sigma = 30;//位数
ins(root[0], 0, 0, 0);//边界
}
//当前版本的root,上个版本的root,值, 当前版本的编号
inline void ins(int &xx, int o, int v, int pos) {
sz[xx = ++ idx] = sz[o] + 1;
ver[idx] = pos;
for(int d = sigma, c, x = xx; d >= 0; d --){
c = (v >> d) & 1;
ch[x][c ^ 1] = ch[o][c ^ 1];
sz[x = ch[x][c] = ++ idx] = sz[o = ch[o][c]] + 1;
ver[idx] = pos;
}
}
inline int query(int x, int y, int d) {
if (d < 0) return 0;
int lsz = sz[ch[y][0]] - sz[ch[x][0]];
//std::cout << d << ' ' << lsz << '\n';
for (int i = 1; i <= m; i ++)
lsz += sz[ch[ry[i]][0]] - sz[ch[rx[i]][0]];
if (lsz >= 2) {
for (int i = 1; i <= m; i ++) {
ry[i] = ch[ry[i]][0];
rx[i] = ch[rx[i]][0];
}
return query(ch[x][0], ch[y][0], d - 1);
} else {
for (int i = 1; i <= m; i ++) {
if (sz[ch[ry[i]][0]] - sz[ch[rx[i]][0]]) {
ry[i] = ch[ry[i]][0];
rx[i] = ch[rx[i]][0];
} else {
ry[i] = ch[ry[i]][1];
rx[i] = ch[rx[i]][1];
}
}
if (sz[ch[y][0]] - sz[ch[x][0]]) {
rx[++ m] = ch[x][0];
ry[m] = ch[y][0];
}
return (1 << d) + query(ch[x][1], ch[y][1], d - 1);
}
}
}HT;
inline void solve() {
HT.Init();
std::cin >> n;
auto &root = HT.root;
for (int i = 1, x; i <= n; i ++) {
std::cin >> x;
HT.ins(root[i], root[i - 1], x, i);
}
std::cin >> n;
for (int i = 1; i <= n; i ++) {
int l, r;
std::cin >> l >> r;
m = 0;
std::cout << HT.query(root[l - 1], root[r], 30) << '\n';
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_ --) solve();
return 0;
}
结论(logA) + 1的主席树版本
区间两个元素或起来最小的两个元素一定在这个区间前log值域 + 1小里面。同理区间与最大选两个元素一定在这个区间前log值域 + 1大里面。不会证明
#include <bits/stdc++.h>
const int MOD = 1e9 + 7;
const int INF = 2e9;
using ll = long long;
typedef std::pair<int, int> PII;
using namespace std;
int n, m;
const int N = 1e5 + 10, M = 3e5 + 10;
struct node{
int l, r, cnt;
}tr[N * 21];
struct Historical_Segment_Tree{
int idx;
int root[N];
inline int build(int l, int r){
int q = ++ idx;
if(l == r) return q;
int mid = l + r >> 1;
tr[q].l = build(l, mid);//存的是左儿子的编号并非边界
tr[q].r = build(mid + 1, r);
return q;
}
inline int insert(int p, int l, int r, int x, int sum){//需要用到的上一个版本root[i - 1](返回的值是当前版本的root)
int q = ++ idx;
tr[q] = tr[p];//复制上一个节点的信息
if(l == r){
tr[q].cnt += sum;//新版本的信息加1
return q;
}
int mid = l + r >> 1;
if(x <= mid) tr[q].l = insert(tr[p].l, l, mid, x, sum);//在左子树则需要更新信息,否则保留原本信息就可以
else tr[q].r = insert(tr[p].r, mid + 1, r, x, sum);
tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
return q;
}
inline int query(int p, int q, int L, int R, int l, int r){
if(L >= l && R <= r) return tr[q].cnt - tr[p].cnt;
int mid = L + R >> 1;
int cnt = 0;
if(l <= mid) cnt += query(tr[p].l, tr[q].l, L, mid, l, r);
if(r > mid) cnt += query(tr[p].r, tr[q].r, mid + 1, R, l, r);
return cnt;
}
inline int query(int p, int q, int l, int r, int k){//区间k小
if(l == r) return l;
int mid = l + r >> 1;
int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
if(cnt >= k) return query(tr[p].l, tr[q].l, l, mid, k);
else return query(tr[p].r, tr[q].r, mid + 1, r, k - cnt);
}
}HST;
inline void solve() {
std::cin >> n;
auto &root = HST.root;
root[0] = HST.build(1, n);
std::vector<int> number, numbers;
for (int i = 1, x; i <= n; i ++) {
std::cin >> x;
number.push_back(x);
numbers.push_back(x);
}
std::sort(number.begin(), number.end());
for (int i = 1; i <= n; i ++) {
int pos = std::lower_bound(number.begin(), number.end(), numbers[i - 1]) - number.begin() + 1;
root[i] = HST.insert(root[i - 1], 1, n, pos, 1);
}
std::cin >> m;
for (int i = 1; i <= m; i ++) {
int l, r;
std::cin >> l >> r;
std::vector<int> nums;
int q = std::min(r - l + 1, 31);
for (int j = 1; j <= q; j ++) {
int t = HST.query(root[l - 1], root[r], 1, n, j);
nums.push_back(number[t - 1]);
}
int ans = INF;
for (int j = 0; j < nums.size(); j ++) {
for (int k = j + 1; k < nums.size(); k ++)
ans = std::min(ans, nums[j] | nums[k]);
}
std::cout << ans << '\n';
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_ --) solve();
return 0;
}