Loading

ARC142D 做题记录

link

厉害的东西。

考虑先翻译一下原题面的条件:在每一条边只经过一次的前提下,仅存在一种同时移动所有棋子的方案,且对于移动后的局面,恢复原局面是唯一的移动所有棋子的方案。

这个条件太复杂,我们一定是要把它转化掉的。

考虑把树划分为若干条链,每条链长度 \(\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;
}
posted @ 2024-07-11 20:53  Sktn0089  阅读(25)  评论(0)    收藏  举报