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;
}
posted @ 2023-06-23 22:47  春始于雪之下  阅读(29)  评论(0编辑  收藏  举报