棋子会将棋盘分开为若个部分,分裂过程不好维护。故先离线所有询问,将添加棋子改为删除棋子。
为了方便,我们略微更改棋子的等级,使其两两不同。
我们首先考虑 3 类边。我们维护三类边组成的连通块,每个连通块需要维护四个数据结构:
其连到的白子的等级(我们需要查询等级小于等于给定值的棋子个数);
其连到的黑子的等级(我们需要查询等级小于等于给定值的棋子个数);
空位,同一行的空位应当连续(我们需要查询一行的某一段中包含多少空位);
空位,同一列的空位应当连续(我们需要查询一列的某一段中包含多少空位);
同时我们还需要支持合并两个连通块、删除一个棋子。其可以用并查集+线段树合并实现。
接着考虑 2 类边。我们直接维护两个(横向与纵向的)并查集,分别维护 2 类边构成的连通块。为了不与 3 类边算重,我们要去掉:
2 类连通块两端的棋子,如果出现在 3 类连通块中;
2 类连通块内的空白段,将这个段放到 3 类连通块内查询。
最后考虑 1 类边,我们依次检查其相邻的几个位置,如果它们没有在 2 或 3 类边中统计过,则加进答案中。
AC Code: