1. 程式人生 > 其它 >CF EDU 110 D - Playoff Tournament

CF EDU 110 D - Playoff Tournament

D - Playoff Tournament

樹形dp

  1. 將字串顛倒,並把每個結點的左右兒子交換,即可變成二叉樹的形式(本題中讓左右兒子交換)
  2. 首先 dp 算出當前字串每個結點的答案
  3. 注意到修改一個結點時只會影響到他上方一條鏈上的結點,數目為 \(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;
}