牛客多校3 I

16 篇文章 0 订阅

题目大意:

一个长度为 n n n的序列 a a a,对这个序列有 q q q个操作,操作类型如下:

  • [ l , r ] [l,r] [l,r] a i a_i ai异或 x x x
  • [ l , r ] [l,r] [l,r] a i a_i ai异或 ( x + ( i − l ) ) (x+(i-l)) (x+(il))

1 ≤ n ≤ 6 × 1 0 5 , 1 ≤ q ≤ 4 × 1 0 5 , 0 ≤ a i < 2 30 1 \le n \le 6 \times 10^5, 1 \le q \le 4 \times 10^5, 0\le a_i < 2^{30} 1n6×105,1q4×105,0ai<230

解题思路:(来自KeHe

  • 对于第一种操作,可以利用差分来处理
  • 对于第二种操作,设 l o w b i t ( x ) = 2 k lowbit(x) = 2 ^ k lowbit(x)=2k,对于 i − l < 2 k , a ⊕ ( x + i − l ) = a ⊕ x ⊕ ( i − l ) i - l < 2 ^ k, a \oplus (x+i-l) = a \oplus x \oplus (i - l) il<2k,a(x+il)=ax(il)相当于对区间 [ l , l + 2 k ) [l,l+2^k) [l,l+2k),区间异或x后再依次异或上 0 , 1 , 2 , . . . , 2 k − 1 0,1,2,...,2^k-1 0,1,2,...,2k1,第一部分则与操作一同理,第二部分可以先打个标记,记为 b i , k b_{i,k} bi,k,执行完后: x ← x + 2 k , l ← l + 2 k x \leftarrow x + 2^k, l \leftarrow l + 2^k xx+2k,ll+2k, 重复上述操作直到 l + 2 k − 1 > r l+2^k - 1 > r l+2k1>r,此时再从大到小枚举 k k k执行上述操作,填满区间 [ l , r ] [l,r] [l,r]即可
	从大到小枚举k时,不用考虑跟lowbit(x)相等,因为当跟lowbit(x)相等时,在第一次的重复操作时,会l+2^k-1>r
  • b i , k b_{i,k} bi,k相当于 0 , 1 , 2 , . . . , 2 k − 1 − 1 , 2 k − 1 ⊕ 0 , 2 k − 1 ⊕ 1 , . . . , 2 k − 1 ⊕ ( 2 k − 1 − 1 ) 0,1,2,...,2^{k-1}-1,2^{k-1}\oplus0,2^{k-1}\oplus1,...,2^{k-1}\oplus(2^{k-1}-1) 0,1,2,...,2k11,2k10,2k11,...,2k1(2k11),相当于标记 b i , k − 1 b_{i,k-1} bi,k1 b i + 2 k − 1 , k − 1 b_{i+2^{k-1},k-1} bi+2k1,k1,以及区间 [ i + 2 k − 1 , i + 2 k ) [i+2^{k-1},i+2^{k}) [i+2k1,i+2k)再异或上 2 k − 1 2^{k-1} 2k1,(要判断越界)

AC代码:

#include <bits/stdc++.h>
#define ft first
#define sd second
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e6 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
int n, q, s[maxn], pw[20];
bitset <20> b[maxn];
void solve() {
	cin >> n >> q;
	int now, pre = 0;
	for (int i = 1; i <= n; i++) cin >> now, s[i] = now ^ pre, pre = now;;
	int op, l, r, x;
	while (q--) {
		cin >> op >> l >> r >> x;
		if (!op) {
			s[l] ^= x, s[r + 1] ^= x;
			continue;
		}
		for (int i = 0; i <= 19; i++) 
			if ((x & pw[i]) && l + pw[i] - 1 <= r)
				b[l][i].flip(), s[l] ^= x, s[l + pw[i]] ^= x, l = l + pw[i], x = x + pw[i];
		for (int i = 19; i >= 0; i--)
			if (l + pw[i] - 1 <= r)
				b[l][i].flip(), s[l] ^= x, s[l + pw[i]] ^= x, l = l + pw[i], x = x + pw[i];
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 19; j >= 1; j--) {
			if (b[i][j]) {
				b[i][j - 1].flip();
				if (i + pw[j - 1] > n) continue;
				b[i + pw[j - 1]][j - 1].flip();
				s[i + pw[j - 1]] ^= pw[j - 1];
				if (i + pw[j] <= n) s[i + pw[j]] ^= pw[j - 1];
			}
		}
	}
	for (int i = 1; i <= n; i++)
		s[i] ^= s[i - 1], cout << s[i] << " \n"[i == n];
}
int main() {
    IOS;
	pw[0] = 1;
	for (int i = 1; i <= 19; i++) pw[i] = pw[i - 1] * 2;
	solve();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值