ARC142D 做题记录
厉害的东西。
考虑先翻译一下原题面的条件:在每一条边只经过一次的前提下,仅存在一种同时移动所有棋子的方案,且对于移动后的局面,恢复原局面是唯一的移动所有棋子的方案。
这个条件太复杂,我们一定是要把它转化掉的。
考虑把树划分为若干条链,每条链长度 \(\ge 1\)。我们令链头为空,在其他位置放棋子。
那么显然可以找到一种合法的移动方案:每条链的棋子往链头方向移动一步。移完后,再往链尾方向移动一步。
这是否保证移动方案唯一?考虑找出一些必要的条件:
-
两个链尾不能相邻:否则其中一条链上的棋子可以跟着另一条链一起移动。
-
两个链头不能相邻:一次移动后,链头变成了链尾,转化为跟上面一样。
-
链身不能和链头/链尾相邻:若与链尾相邻,后面的棋子可以跟随这条链移动;移动一次后,链头变成链尾,同理。
满足这三个条件,即可完成棋子摆放到链划分的双射,可以直接 dp:设 \(f[u,0\sim 7]\) 表示对于 \(u\) 及其子树,以下每一种情况时的方案数:
-
\(u\) 为单独一个链头
-
\(u\) 为一个链头,接着一条完整的链
-
\(u\) 为单独一个链尾
-
\(u\) 为一个链尾,接着一条完整的链
-
\(u\) 为链身,下方只接着链头
-
\(u\) 为链身,下方只接着链尾
-
\(u\) 为链身,下方接着链头和链尾(即这是一条完整的链)
-
\(u\) 为单独一个链身
转移一坨即可。
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define fi first
#define se second
#define pir pair <ll, ll>
#define mkp make_pair
#define pb push_back
using namespace std;
const ll maxn = 2e5 + 10, mod = 998244353;
ll n, f[maxn][8], g[8];
vector <ll> to[maxn];
void dfs(ll u, ll fa = 0) {
f[u][0] = f[u][2] = f[u][7] = 1;
for(ll v: to[u])
if(v ^ fa) {
dfs(v, u);
g[0] = g[1] = g[2] = g[3] = g[4] = g[5] = g[6] = g[7] = 0;
g[0] += f[u][0] * f[v][3];
g[1] += f[u][0] * (f[v][2] + f[v][5]);
g[1] += f[u][1] * f[v][3];
g[2] += f[u][2] * f[v][1];
g[3] += f[u][2] * (f[v][0] + f[v][4]);
g[3] += f[u][3] * f[v][1];
g[4] += f[u][4] * f[v][6];
g[6] += f[u][4] * (f[v][2] + f[v][5]);
g[5] += f[u][5] * f[v][6];
g[6] += f[u][5] * (f[v][0] + f[v][4]);
g[6] += f[u][6] * f[v][6];
g[7] += f[u][7] * f[v][6];
g[4] += f[u][7] * (f[v][0] + f[v][4]);
g[5] += f[u][7] * (f[v][2] + f[v][5]);
for(ll i = 0; i <= 7; i++)
f[u][i] = g[i] %mod;
}
}
int main() {
scanf("%lld", &n);
for(ll i = 1; i < n; i++) {
ll u, v; scanf("%lld%lld", &u, &v);
to[u].pb(v), to[v].pb(u);
}
dfs(1);
printf("%lld", (f[1][1] + f[1][3] + f[1][6]) %mod);
return 0;
}

浙公网安备 33010602011771号