我们对于每一个查询操作离线下来,然后按照查询区间的 l所在的块为第一关键字,按 r 为第二关键字排序。排序后维护一个 [L,R],表示已经处理好的区间,对于当前查询的区间,直接暴力跳过去,记录下答案即可。
这个东西大概长成这个样子:
while (R<r) Add(++R);
while (R>r) Delete(R--);
while (L>l) Add(--L);
while (L<l) Delete(L++);
//其中Add()表示加贡献,Delete()表示减贡献
模板题:P1494
掌握上述流程之后其实就不难。
设当前要加或减的袜子的颜色是 c,则它所带来的贡献就是 cnt_c,其中 cnt_c 表示颜色 c 的数量。(这个可以组合数把式子展开来证,但是可以简单一点:多出来的贡献可定是要钦定选择当前的袜子的,那么为了配对成功,就需要从之前的颜色为 c 的袜子里选一个,所以有 cnt_c 种选择,贡献就是 cnt_c。)弄一个全局的 ans 来记录答案就行。
复杂度
R 的移动
首先对于每一个块,求出其第一个询问的答案需要 O(n) 的时间,由于同一个块中的 r 是排好序的,所以在同一个块中从第一个求到最后一个 r 需要 O(n) 的时间,那么 \sqrt{n} 个块就是 O(n\sqrt{n})。
块与块之间的 r 是无序的,所以每次移动最坏为 O(n) 的,但由于块是排好序的,且有 \sqrt{n} 个块,所以这样的移动最多只有 O(\sqrt{n}) 次,复杂度为 O(n\sqrt{n})。