P6803 [CEOI2020]星际迷航
Des
P6803 [CEOI2020]星际迷航
Sol
首先设败点为先手必败的点,胜点为先手必胜的点。记胜点
在不同树之间连边相当于给被连上的树换一个根。
如果一个点接上一个败点,那么自己有可能变成胜点,从而导致根的胜负情况发生逆转(变成胜点或败点都有可能)。如果一个点接上一个胜点那么没有影响。
于是换根 DP 求出有多少个点连接败点后会使根的胜负情况发生逆转,记为
记整棵树败点数目为
然后考虑
考虑
对于
我们设转移矩阵为
然后讲一下换根 DP。
最后复杂度
这道题比较麻烦地方在于
My Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, D = 1e9 + 7;
inline void Add(int &a, ll b) { a = (a + b) % D; }
int n, c;
ll K;
struct mat {
int a[2][2];
mat(bool e = false) {
memset(a, 0, sizeof a);
if(e) a[0][0] = a[1][1] = 1;
}
mat operator*(const mat &x) {
mat y;
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
y.a[i][j] = ((ll)a[i][0] * x.a[0][j] + (ll)a[i][1] * x.a[1][j]) % D;
return y;
}
};
mat fpm(mat a, ll b) {
mat res(true);
while(b) {
if(b & 1) res = res * a;
b >>= 1, a = a * a;
}
return res;
}
vector<int> g[N];
int dp[N], s0[N], R[N], SR[N][2];
// SR[i][0] 表示 i 号点所有 dp 值为 0 的儿子的 R 的和,SR[i][1] 同理
void dfs1(int u, int f) {
for(int v : g[u]) {
if(v == f) continue;
dfs1(v, u);
s0[u] += !dp[v];
SR[u][dp[v]] += R[v];
}
dp[u] = (s0[u] > 0);
if(s0[u] == 1) R[u] = SR[u][0];
else if(s0[u] == 0) R[u] = SR[u][1] + 1;
}
int dp2[N], R2[N];
// 换根 DP
void dfs2(int u, int f) {
if(!dp[u]) ++c;
R2[u] = R[u], dp2[u] = dp[u];
for(int v : g[u]) {
if(v == f) continue;
int s0u = s0[u], dpu = dp[u], Ru = R[u], SRu0 = SR[u][0], SRu1 = SR[u][1];
int s0v = s0[v], dpv = dp[v], Rv = R[v], SRv0 = SR[v][0], SRv1 = SR[v][1];
s0[u] -= !dp[v];
dp[u] = (s0[u] > 0);
SR[u][dp[v]] -= R[v];
if(s0[u] == 1) R[u] = SR[u][0];
else if(s0[u] == 0) R[u] = SR[u][1] + 1;
else R[u] = 0;
dp[v] |= !dp[u];
s0[v] += !dp[u];
SR[v][dp[u]] += R[u];
if(s0[v] == 1) R[v] = SR[v][0];
else if(s0[v] == 0) R[v] = SR[v][1] + 1;
else R[v] = 0;
dfs2(v, u);
s0[u] = s0u, dp[u] = dpu, R[u] = Ru, SR[u][0] = SRu0, SR[u][1] = SRu1;
s0[v] = s0v, dp[v] = dpv, R[v] = Rv, SR[v][0] = SRv0, SR[v][1] = SRv1;
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n >> K;
for(int i = 1, u, v; i < n; i++) {
cin >> u >> v;
g[u].emplace_back(v), g[v].emplace_back(u);
}
dfs1(1, 0);
dfs2(1, 0);
mat p, T;
// 处理转移矩阵
for(int i = 1; i <= n; i++) {
if(dp2[i] == 0) {
Add(T.a[0][0], n - R2[i]);
Add(T.a[1][0], n);
Add(T.a[0][1], R2[i]);
} else if (dp2[i] == 1) {
Add(T.a[0][0], R2[i]);
Add(T.a[0][1], n - R2[i]);
Add(T.a[1][1], n);
}
}
p.a[0][0] = c, p.a[0][1] = n - c;
p = p * fpm(T, K - 1);
int f0 = p.a[0][0], f1 = p.a[0][1];
if(dp[1]) cout << ((ll)(n - R2[1]) * f0 + (ll)n * f1) % D << '\n';
else cout << (ll)R2[1] * f0 % D << '\n';
return 0;
}