s(長度 n ≤ 3e5),包含小寫字母和 '.'。s[x] 改為字符 c,每次修改後輸出 f(s) = 消除所有連續 ".." 所需最小替換次數(每次替換將最左邊的 ".." 變為 ".")。m ≤ 3e5 次查詢。f(s) 等於所有最大連續點段長度 len 的 len-1 之和。
因為長度為 L 的點段需 L-1 次替換才能變為單點。
維護全局答案 `ans`。修改位置 `pos` 時,僅影響 `pos` 及其左右相鄰的點段邊界。
設 `old = s[pos]`,`nw = c`。
#include
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m; cin >> n >> m;
string s; cin >> s;
long long ans = 0;
for (int i = 0; i < n; ) {
if (s[i] == '.') {
int j = i;
while (j < n && s[j] == '.') ++j;
ans += j - i - 1;
i = j;
} else ++i;
}
while (m--) {
int pos; char c; cin >> pos >> c; --pos;
if (s[pos] != c) {
if (s[pos] == '.') { // 移除點
if (pos > 0 && s[pos-1] == '.') --ans;
if (pos+1 < n && s[pos+1] == '.') --ans;
} else if (c == '.') { // 新增點
if (pos > 0 && s[pos-1] == '.') ++ans;
if (pos+1 < n && s[pos+1] == '.') ++ans;
}
s[pos] = c;
}
cout << ans << "\n";
}
return 0;
}
維護區間:
`sum` = 區間內替換次數,`l` = 左端是否為點,`r` = 右端是否為點。
合併:`sum = L.sum + R.sum + (L.r && R.l)`。
單點更新、查詢整條鏈 `sum`,複雜度 O(log n),常數較大但通用。
O(n),每查詢 O(1),總 O(n+m)。O((n+m) log n)。