这次怎么打成区了。
赛后复盘
首先我的情况应该是 80% 做 E,60% 做 F。
赛时 30min 写完前四题后第三题并没有过。看到 E 是期望,F 是串串,认为磕期望比磕 F 好。当时以为做一个题再调 C 是绝对不劣的,所以我就把 C 扔开了。
随后 21:10 时写完了 E,但是调题的本事确实不是很强。看了一眼 F,发现会了。调 E?还是写 F?其实已经看见群里说 G<F<E 及 D<C 了,但是(不知道是不是因为集训刚学)死都要做出来 E。
拍了 15min 后被高人指导了。说 set 会 TLE,那我改成 vector 呗。
拜谢 HI 未来队长 xtk,我宣布我这次打爆是因为你带歪榜了!!
C 题解
直接暴力加一堆玄学优化显然是过不去的,不知道为什么有素质低的人一直在误导。
先来了解异或的一个性质,\(x\oplus y=z\),则 \(z\oplus y=x,z\oplus x=y\),其中 \(\oplus\) 是按位异或。所以操作一等价于让 \(ans\) 变成 \(ans\oplus a_i\oplus (a_i-1)\),然后再让 \(a_i\leftarrow a_i-1\)。
我们发现操作二只有 \(\ge 1\) 的数才会被操作到。我们维护一个数据结构,使得一个数 \(\ge 1\) 就在里面,然后每次操作就遍历一遍这个数据结构,如果变成 \(0\) 了就弹出去。set 确实能做,但是有 \(O(N)\) 的 vector 神力。
这里重点说一下如何去避免删了 \(a_i\) 后迭代器跑不到下一个数的问题。再定义一个 vector,然后把保留的放到第二个里,最后再交换即可。
时间复杂度证明不显然。我们发现每个数在 vector 被遍历的次数等价于它被操作一加一的次数,所以时间复杂度是 \(O(N+Q)\)。这里插一嘴,set 的时间复杂度是 \(O(N\log N+Q\log Q)\)。
代码如下。
#include<iostream>
#include<vector>
using namespace std;
int read() {
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int N = 5e5 + 10;
int n,q;
int a[N];
vector<int> v,tmp;
bool vis[N];
int sxor;
int main() {
n = read(),q = read();
for (int i = 1; i <= q; i++) {
int op = read();
if (op == 1) {
int x = read();
sxor ^= a[x];
a[x]++;
sxor ^= a[x];
if (a[x] == 1) {
v.push_back(x);
}
}
else {
sxor = 0;
while (v.size()) {
int x = v.back();
v.pop_back();
a[x]--;
if (a[x]) {
tmp.push_back(x);
}
sxor ^= a[x];
}
swap(v,tmp);
}
cout << sxor << "\n";
}
return 0;
}
浙公网安备 33010602011771号