题解:uoj671【UNR #5】诡异操作

饺子醋环节。

题意:给出一个 \(n\) 长序列 \(a\),有三种操作:

  1. 区间除法。

  2. 区间取与。

  3. 区间求和。

\(n\le 2\times 10^5,V< 2^{128}\)。题面给了一份输入输出模板。

做法:

首先直接做考虑每个点维护一下 \(2^k\) 出现了多少次,一操作直接做总复杂度是 \(O(n\log V)\) 的,二操作采用直接打 tag,复杂度 \(O(n\log n)\),pushup 是 \(\log V\) 的,所以总复杂度是 \(O(n\log^2V)\)

考虑怎么优化,除法和 tag 感觉没什么可以优化的了,考虑怎么优化 pushup。观察 pushup 的本质是什么,我们把 \(2^k\) 出现次数展开成二进制写,其实我们是维护了一个 \(\log V\times \log n\) 的矩阵,那么我们其实没有必要去维护 \(\log V\) 这边,而是维护 \(\log n\) 这一边即可,复杂度降到 \(n\log n\log V\),可以通过。没有太看懂网上题解对于除法说精细实现可以做到 \(n\log V\) 的解释,我一直整体除以 \(2\) 不是爆炸完了。

有点卡常,一个可行的卡常方法是把每个节点只考虑前若干个有值得行,这样会快特别多。我这里比较暴力,直接把长度补成 \(2^n\) 然后就比较方便定长度。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define u128 __uint128_t
const int maxn = 5.3e5 + 5;
typedef __uint128_t u128;
inline u128 read() {
    static char buf[100];
    scanf("%s", buf);
    // std::cin >> buf;
    u128 res = 0;
    for(int i = 0;buf[i];++i) {
        res = res << 4 | (buf[i] <= '9' ? buf[i] - '0' : buf[i] - 'a' + 10);
    }
    return res;
}
inline void output(u128 res) {
    if(res >= 16)
        output(res / 16);
    putchar(res % 16 >= 10 ? 'a' + res % 16 - 10 : '0' + res % 16);
    //std::cout.put(res % 16 >= 10 ? 'a' + res % 16 - 10 : '0' + res % 16);
}
struct node {
	vector<u128> a;
	bool f;
	inline u128& operator[](int x) {
		return a[x];
	}
	void resize(int N) {
		a.resize(N);
	}
	inline int size() {
		return a.size();
	}
} ;
int n, q;
u128 a[maxn], mx, pw = 1;
int len[maxn];
struct Segtree {
	node tr[maxn << 2];
	u128 tag[maxn << 2];
	void pushup(int t) {
		tr[t].f = tr[t << 1].f & tr[t << 1 | 1].f;
		u128 val = 0;
		for (int i = 0; i < tr[t << 1].size(); i++) {
			tr[t][i] = val ^ tr[t << 1][i] ^ tr[t << 1 | 1][i];
			val = (tr[t << 1][i] & tr[t << 1 | 1][i]) | (tr[t << 1][i] & val) | (val & tr[t << 1 | 1][i]);
		}
		tr[t][tr[t].size() - 1] = val;
	}
	void build(int l, int r, int t) {
		tag[t] = mx;
		tr[t].resize(len[r - l + 1]);
		//cout << l << " " << r << "asdf " << len[r - l + 1] << endl;
		if(l == r) {
			tr[t][0] = a[l];
			tr[t].f = a[l] == 0;
			return ;
		}
		int mid = l + r >> 1;
		build(l, mid, t << 1), build(mid + 1, r, t << 1 | 1);
		pushup(t);
	}
	void addtag(int t, u128 val) {
		tag[t] &= val;
		for (int i = 0; i < tr[t].size(); i++)
			tr[t][i] &= val;
	}
	void pushdown(int t) {
		if(tag[t] == mx)
			return ;
		addtag(t << 1, tag[t]);
		addtag(t << 1 | 1, tag[t]);
		tag[t] = mx;
	}
	void update1(int l, int r, int x, int y, int t, u128 v) {
		if(tr[t].f)
			return ;
		if(l == r) {
			tr[t][0] /= v;
			tr[t].f = tr[t][0] == 0;
			return ;
		}
		int mid = l + r >> 1;
		pushdown(t);
		if(x <= mid)
			update1(l, mid, x, y, t << 1, v);
		if(mid < y)
			update1(mid + 1, r, x, y, t << 1 | 1, v);
		pushup(t);
	}
	void update2(int l, int r, int x, int y, int t, u128 v) {
		if(tr[t].f)
			return ;
		if(x <= l && r <= y) {
			addtag(t, v);
			return ;
		}
		int mid = l + r >> 1;
		pushdown(t);
		if(x <= mid)
			update2(l, mid, x, y, t << 1, v);
		if(mid < y)
			update2(mid + 1, r, x, y, t << 1 | 1, v);
		pushup(t);
	}
	u128 query(int l, int r, int x, int y, int t) {
		if(x <= l && r <= y) {
			u128 res = 0;
			for (int i = 0; i < tr[t].size(); i++)
				res += tr[t][i] << i;
			return res;
		}
		int mid = l + r >> 1;
		pushdown(t);
		if(y <= mid)
			return query(l, mid, x, y, t << 1);
		if(mid < x)
			return query(mid + 1, r, x, y, t << 1 | 1);
		return query(l, mid, x, y, t << 1) + query(mid + 1, r, x, y, t << 1 | 1);
	}
} tree;
signed main() {
	cin >> n >> q;
	for (int i = 1; i <= n; i++)
		a[i] = read();
	for (int i = 0; i < 128; i++)
		mx += pw, pw <<= 1;
	int tn = 1, cnt = 1;
	while(tn < n)
		len[tn] = cnt, tn <<= 1, cnt++;
	len[tn] = cnt;
	n = tn;
	tree.build(1, n, 1);
	while(q--) {
		int op, l, r; u128 v;
		cin >> op >> l >> r;
		if(op == 1) {
			v = read();
			if(v != 1)
				tree.update1(1, n, l, r, 1, v);
		}
		if(op == 2)
			v = read(), tree.update2(1, n, l, r, 1, v);
		if(op == 3)
			output(tree.query(1, n, l, r, 1)), putchar('\n');
	}
	return 0;
}
posted @ 2025-11-02 12:00  LUlululu1616  阅读(26)  评论(0)    收藏  举报