「S2OI Round 1」三叉求和
前言
评级建议:上位蓝或下位紫。
解法貌似跟官解并不一样。
思路
考虑拆贡献,对于节点
原因是简单的,因为
由于,要求对于路径上所有点的权值和为
而每个点对路径上剩余点的贡献的和可通过等比数列求和公式进一步化简:
设路径经过的点选择的儿子节点分别为
令
问题转化为对于
这是容易 DP 的!枚举
转移是
考虑如何去掉外层的枚举,不难发现,在这个转移中
令
时间复杂度
代码
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
using i64 = long long;
using pii = pair<int, int>;
const int maxn = 2005, mod = 1e9 + 7;
int n;
string s;
int dp[2][14005][3], pw[maxn];
int main() {
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin >> n >> s;
reverse(s.begin(), s.end());
while (s.size() < max(8, n + 1)) s += '0';
pw[0] = 1;
for (int i = 1; i < 8; i ++)
pw[i] = pw[i - 1] * 3;
memset(dp, 0, sizeof dp);
dp[1][7000][0] = 1;
for (int i = 0; i < s.size(); i ++) {
for (int j = 0; j <= 7000 + i * 2; j ++)
for (int k : {0, 1, 2}) dp[i & 1][j][k] = 0;
for (int j = 0; j <= 7000 + i * 2; j ++)
for (int k : {0, 1, 2}) {
if (!dp[~i & 1][j][k]) continue;
for (int t : {0, 1, 2}) {
if (s[i] != '?') {
int u = 2 * (s[i] - '0') + t + k;
if (u % 3 && (!i || i > n) || i >= 8 && t) continue;
(dp[i & 1][j + u % 3 - t * pw[i]][u / 3] += dp[~i & 1][j][k]) %= mod;
} else {
for (int o : {0, 1, 2}) {
int u = 2 * o + t + k;
if (u % 3 && (!i || i > n) || i >= 8 && t) continue;
(dp[i & 1][j + u % 3 - t * pw[i]][u / 3] += dp[~i & 1][j][k]) %= mod;
}
}
}
}
}
cout << dp[~s.size() & 1][7000][0] << endl;
return 0;
}