CF1923(重要)
只做了 A,成功被 sb 错误卡住。
每次挑最右边的左移。
每次一定是优先向最近的怪物打,打完一个打下一个最近的。
子弹不一定只能打两个怪物,所以打的时候用循环判断子弹是否打完。
-
l = r 不行
-
否则考虑全 1 再把所有 \(c_i=1\) 的都 +1,这需要 \(cnt1[r]-cnt1[l-1]+(r-l+1)\),\(cnt1[i]\) 表示前 \(i\) 个中 \(1\) 的个数。
如果 \(s[r]-s[l-1]<\text{上面的式子}\),就可以放下;否则就是不能。
注意是 \(<\) 不是 \(\le\)。
先对于每个位置求出 \(l,r\):\(l[i]\) 表示与 \(a_i\) 相等的数字段左端点在哪里,\(r[i]\) 表示右端点。
对一个位置 \(x\) 的答案分成两类:它左边的吃掉它和它右边的吃掉它,这两类是类似的。
对于它左边的吃掉它,我们先二分一个位置 \(pos\):\(pos\) 是最大的使得 \(a_pos+\dots+a_{x-1}>a_x\) 的位置。
如果 \(pos\sim x-1\) 不是全相等,那就可以用 \(x-pos\) 次吃掉。
如果 \(1\sim x-1\) 全相等,则“它左边的吃掉它”这一类无解。
否则就是 \(l[pos]\sim x-1\) 都是相等的,所以让 \(a_{l[pos]}\) 吃掉 \(a_{l[pos]-1}\) 再一路吃过去。
void srh(int v, int fa) {
int u = cntc[c[v]], s = up[c[v]];
ans += s;
for (int i = 0; i < e[v].size(); i++) {
if (e[v][i] == fa)
continue;
up[c[v]] = 0;
srh(e[v][i], v);
}
ans += cntc[c[v]] - u;
cntc[c[v]] = u + 1;
up[c[v]] = s + 1;
}
\(c[x]\) 表示 \(x\) 的颜色,\(cntc[clr]\) 记录当前有多少个已经回溯了的 \(clr\) 色的点上方没有已经回溯的 \(clr\) 色的点,\(up[clr]\) 表示当前有多少个 已经回溯了的 是\(clr\)色的 上方没有\(clr\)色的 不在\(v\)子树中的 结点。
代码解释:
ans += cntc[c[v]] - u
,\(u\) 是没遍历这颗子树时“裸露”的 \(c[v]\) 个数,\(cntc[c[v]]\) 是现在“裸露”的 \(c[v]\) 个数,所以 \(cntc[c[v]]-u\) 就是这颗子树内有多少个裸露的 \(c[v]\),都可以与 \(v\) 组成好路径。
ans += s
,这些兄弟结点与当前节点都能组成好路径。
up[c[v]] = 0
是因为马上我们就要递归进子节点了,因为对于 \(v\) 的子节点 \(s\) 来说,所有 \(s\) 的颜色为 \(clr\) 的兄弟结点都不是“裸露”着的了,于是 \(up\) 改为 \(0\)。