#前缀和 ## 题目大意 给定 $Q$ 次操作,每一次操作都是往集合里添加一个数,如果已存在就删去这个数。每一次操作之后令集合大小为 $|S|$,那么所有 $j\in S$,$A_j$ 都要加上 $|S|$。 ## 解法 我们发现,加入一个元素到集合里和删除这个元素使非常迅速的,而拖慢我们暴力思路的元凶是统计答案,如果一个元素一直在这个集合里出现的话我们每一次都要进行相加。 因此我们需要对集合数量做一个前缀和,然后找出每一个元素是什么时候进来、什么时候出去的,方便统计这个元素对答案的贡献,然后加入到答案数组里就行了。 对于元素 $x$,$i$ 时插入进去,$j$ 时删除掉,那么对答案 $a_x$ 的贡献就是 $s_{j-1}-s_{i-1}$。注意有些元素可能最后没有被删除,那么就需要进行特殊处理。 ## 代码 ```cpp #include <iostream> using namespace std; using LL = long long; const LL kMaxN = 2e5 + 5; LL idx[kMaxN], s[kMaxN], a[kMaxN], n, q, sz; int main() { cin >> n >> q; for (LL i = 1, x; i <= q; i++) { cin >> x; if (!idx[x]) { // 如果没有出现 idx[x] = i; // 记录出现的位置 sz++; // 记录集合大小 } else { // 如果已经出现过 a[x] += s[i - 1] - s[idx[x] - 1]; // 计算贡献 idx[x] = 0; // 删除这个数 sz--; // 记录集合大小 } s[i] = s[i - 1] + sz; // 计算前缀和 } for (LL i = 1; i <= n; i++) { // 输出答案 cout << a[i] + (idx[i] ? s[q] - s[idx[i] - 1] : 0) << ' '; } return 0; } ```