HDU7152. Copy (2022杭电多校第2场1003)
HDU7152. Copy (2022杭电多校第2场1003)
题意
给定一个序列 \(a_1,a_2,\dots,a_n\),共有 \(q\) 次操作,每次操作有两种类型:
- 操作1
(1,l,r)
表示复制区间 \([l,r]\) 的内容,在区间 \([l,r]\) 的末尾处插入这段内容。 - 操作2
(2,x)
询问当前 \(a_x\) 的值。
只需要输出所有操作2答案的异或和即可,保证操作1的数量不超过 \(20000\) 次,初始序列长度不超过 \(10^5\),操作的 \(l,r,x\) 均不超过初始序列长度。
分析
首先对于某个询问,理论上是可以通过之前的操作推出其实际询问的是初始序列中的第几个数。
又注意到,题目只要求输出异或和,故而初始序列中的每个数只有贡献奇数次(相当于 \(1\) 次)和贡献偶数次的区别(相当于没有贡献)。于是可以使用一个 \(01\) 串来描述初始序列中每个数的贡献(显然可以用 bitset 存)。
于是有了一个乱搞做法:
考虑离线,倒序处理操作,同时维护一个上述 \(01\) 串。
一开始这个串全 \(0\),
遇到一个 \(2\) 操作把对应位取反。
遇到一个 \(1\) 操作,因为进行一次操作 \(1\) 之后,对于 \([1,r]\) 中的位没有影响,对于 \([r+1,n]\) 中的位相当于在询问 \([r+1-(r-l+1),n-(r-l+1)]\) 位的情况,所以可以把 \([r+1,n]\) 位的子串向低位移 \(r-l+1\) 位,与 \([1,r]\) 的子串进行异或(因为算的是奇数贡献),用异或的结果更新维护的 \(01\) 串。
考虑完所有的操作之后,得到的 \(01\) 串就是,初始序列中每个数的贡献,循环一遍统计一下就行。
维护的过程可以使用 bitset,这样原来 \(O(20000n)\) 的不可行的复杂度将会变成 \(O(\frac{20000n}{w})\) 就变得可行了。
题外话:
比赛时,个人猜测可能由于HDU OJ只支持一个文件的数据,导致多组数据不太能出到极限数据(否则组数太少了,容易被奇怪的办法干过去),导致比赛时这题暴力也能通过。
代码
#include <bitset>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 10;
int n, q;
int a[maxn];
bitset<maxn> S, l, r, tmp;
int op[maxn][3];
void solve() {
S.reset();
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= q; i++) {
cin >> op[i][0];
if (op[i][0] == 1) {
cin >> op[i][1] >> op[i][2];
} else {
cin >> op[i][1];
}
}
for (int i = q; i >= 1; i--) {
if (op[i][0] == 1) {
tmp.set();
tmp <<= (op[i][2] + 1);
l = ~tmp & S;
r = tmp & S;
S = l ^ (r >> (op[i][2] - op[i][1] + 1));
} else {
S.flip(op[i][1]);
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (S[i]) {
ans ^= a[i];
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
另解
可以使用类似块状链表这种数据结构维护,GCC有个黑科技是rope,不属于STL,属于拓展部分,对于这道题来说算是神器。
代码
#include <ext/rope>
#include <iostream>
using namespace std;
using namespace __gnu_cxx;
rope<int> S;
void solve() {
int n, q;
cin >> n >> q;
S.clear();
for (int i = 1; i <= n; i++) {
int t;
cin >> t;
S.push_back(t);
}
int ans = 0;
while (q--) {
int op, l, r, x;
cin >> op;
if (op == 1) {
cin >> l >> r;
S = S.substr(0, r) + S.substr(l - 1, n - r);
} else {
cin >> x;
ans ^= S[x - 1];
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}