树形dp
- 将字符串颠倒,并把每个结点的左右儿子交换,即可变成二叉树的形式(本题中让左右儿子交换)
- 首先 dp 算出当前字符串每个结点的答案
- 注意到修改一个结点时只会影响到他上方一条链上的结点,数目为 \(logn\) 级别,因此每次修改复杂度为 \(logn\)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#define rs (u << 1)
#define ls (u << 1 | 1)
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll INF = 1e18;
string s;
int f[N];
int k, n;
int dp(int u)
{
//注意递归出口不要越界字符串数组
if (u > n)
return 1;
if (f[u] >= 0)
return f[u];
if (s[u] == '0')
return f[u] = dp(ls);
if (s[u] == '1')
return f[u] = dp(rs);
return f[u] = dp(ls) + dp(rs);
}
void modify(int u, char c)
{
if (u <= 0)
return;
s[u] = c;
if (c == '0')
f[u] = dp(ls);
else if (c == '1')
f[u] = dp(rs);
else
f[u] = dp(ls) + dp(rs);
modify(u >> 1, s[u >> 1]);
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
memset(f, -1, sizeof f);
cin >> k >> s;
n = s.size();
reverse(s.begin(), s.end());
s = " " + s;
dp(1);
int q;
cin >> q;
while(q--)
{
int p;
char c;
cin >> p >> c;
modify(n + 1 - p, c);
cout << f[1] << endl;
}
return 0;
}