线段树+鸽巢原理+位运算+贪心,MT3069宝石的魔力波动

目录

一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

二、解题报告

1、思路分析

2、复杂度

3、代码详解


一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

码题集OJ-宝石的魔力波动 (matiji.net)


二、解题报告

1、思路分析

值域是10^10,也就是34位数

根据鸽巢原理,35个数字一定有两个数有公共位

位与运算贪心原则是:与的数越多,最后的结果越小

对于一个数组b,我们如何求其最大与和?

暴力方法:枚举(i, j)对,取最大b[i] & b[j],时间复杂度O(N^2)

鸽巢原理优化:取前logU大个数,暴力枚举枚举(i, j)对,取最大b[i] & b[j],时间复杂度O(log^2 U)

题目给了512M的空间,那么我们考虑线段树来维护

每个节点维护一个长度不超过35的向量,pushup操作即归并排序取前 min(l.sz + r.sz, B) 大的

查询结果即上面对于数组的最大与和的获取

2、复杂度

时间复杂度: O(m logm logU)空间复杂度: O(m logm logU)

3、代码详解

 ​
#include <bits/stdc++.h>

// #define DEBUG

using i64 = long long;
using u32 = unsigned int;
using u64 = unsigned long long;

template <class Info, class Tag>
struct LazySegmentTree {
    const int n;
    std::vector<Info> info;
    std::vector<Tag> tag;
    LazySegmentTree(int _n) : n(_n), info(2 << (32 - __builtin_clz(n))), tag(2 << (32 - __builtin_clz(n))){}
    
    void pull(int p) {
        info[p] = info[p << 1] + info[p << 1 | 1];
    }

    void apply(int p, const Tag &t) {
        info[p].apply(t);
        tag[p].apply(t);
    }

    void push(int p) {
        apply(p << 1, tag[p]);
        apply(p << 1 | 1, tag[p]);
        tag[p] = Tag();
    }

    template<class T>
    LazySegmentTree(std::vector<T>& _init): LazySegmentTree(_init.size()) {
        auto build = [&](auto&& self, int p, int l, int r) {
            if (l == r) {
            	info[p] = { {_init[l]}, 1 };
                return;
            }
            int mid = l + r >> 1;
            self(self, p << 1, l, mid), self(self, p << 1 | 1, mid + 1, r);
            pull(p);
        };
        build(build, 1, 0, n - 1);
    }

    Info rangeQuery(int p, int l, int r, int x, int y) {
        if (l > y || r < x) return Info();
        if (x <= l && r <= y) 
            return info[p];
        push(p);
        int mid = l + r >> 1;
        return rangeQuery(p << 1, l, mid, x, y) + rangeQuery(p << 1 | 1, mid + 1, r, x, y);
    }

    Info rangeQuery(int l, int r) {
        return rangeQuery(1, 0, n - 1, l, r);
    }

    void rangeApply(int p, int l, int r, int x, int y, const Tag &v){
        if (l > y || r < x) return;
        if (x <= l && r <= y) { 
            apply(p, v);
            return;
        }

        int mid = l + r >> 1;
        push(p);
        rangeApply(p << 1, l, mid, x, y, v);
        rangeApply(p << 1 | 1, mid + 1, r, x, y, v);
        pull(p);
    }

    void rangeApply(int l, int r, const Tag &v) {
        rangeApply(1, 0, n - 1, l, r, v);
    }
};

constexpr int B = 35;

struct Tag {
    i64 add = 0;

    void apply(const Tag& t) {
        add += t.add;
    }
};

struct Info {
	std::vector<i64> b;
    int act = 0;
    void apply(const Tag &t) {
    	for (i64 &x : b)
    		x += t.add;
    }
};

Info operator + (const Info &x, const Info &y) {
	int act = std::min(B, x.act + y.act);
	std::vector<i64> b;
	int i = 0, j = 0;
	while (b.size() < act && i < x.b.size() && j < y.b.size())
		if (x.b[i] > y.b[j])
			b.push_back(x.b[i ++]);
		else
			b.push_back(y.b[j ++]);

	while (b.size() < act && i < x.b.size())
		b.push_back(x.b[i ++]);
	while (b.size() < act && j < y.b.size())
		b.push_back(y.b[j ++]);

    return Info{ b, act };
}

void solve(){
	int n, m;
	std::cin >> n >> m;

	std::vector<i64> a(n);
	for (int i = 0; i < n; ++ i) std::cin >> a[i];

	LazySegmentTree<Info, Tag> sgt(a);

	for (int i = 0, op, l, r; i < m; ++ i) {
		std::cin >> op >> l >> r;
		if (op == 2 && l == r) {
			std::cout << -1 << '\n';
			continue;
		}
		if (op == 1) {
			i64 v;
			std::cin >> v;
			sgt.rangeApply(l - 1, r - 1, Tag{v});
		}
		else {
			auto b = sgt.rangeQuery(l - 1, r - 1).b;
			i64 ma = 0;
			for (int i = 0; i < b.size(); ++ i)
				for (int j = i + 1; j < b.size(); ++ j)
					ma = std::max(ma, b[i] & b[j]);
			std::cout << ma << '\n';
		}
	}
}

auto FIO = []{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);
	return 0;
}();

int main() {
	#ifdef DEBUG
		freopen("in.txt", 'r', stdin);
		freopen("out.txt", 'w', stdout);
	#endif
	int t = 1;
	// std::cin >> t;
	while(t --)
		solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值