CF1276D Tree Elimination

题意:给你一棵树,给定了每条边的顺序,你需要按照顺序对每条边做如下操作

  • 若两个端点都存在,则任意删除一个放入序列
  • 若两个端点有一个被删除里,那么跳过这条边

问有多少种这样的序列 $n\leq 2*10^5$

首先吐槽下官方题解好多有误(不仔细的俄罗斯佬,还不给标程)

一条链的情况

自行推导

树上

对于一个点,我们把边分为$3$种。

  • 与父亲的那条边$p$, $(id_p$=这条边出现的顺序$)$
  • 与儿子连的边$(id_{v_i}>id_p),i\in [1,d]$
  • 与儿子连的边$(id_{v_i}>id_p),i\in [d+1,sum]$

设$dp[u][3]$

  • $dp[u][0]$是在第二种边,导致$u$进入队列,提前删除
  • $dp[u][1]$是在第一种边,导致$u$进入队列,正好被父边删除
  • $dp[u][2]$是在第三种边,导致$u$进入队列或者$u$无法进入队列。延迟删除或干脆不删除

分析$dp[u][0]$ ,从第二种边里取出一条边,轮到这条边的时候删除$v$的父亲$u$,此时$v$就处于了$dp[v][2]$这种情况,只能让后来的边将它删除,或者无法删除。

在这条边时间之前的儿子边都不能在操作的时候删除自己的父亲$u$,或者根本不能操作由于提前被删除了,则$dp[v][0]+dp[v][1]$

在这条边时间之后的儿子边都无法删除自己的父亲$u$,等待之后自己的儿子被删除的命运,或者根本不能操作由于提前被删除了,则$dp[v][0]+dp[v][2]$

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <vector>
#include <utility>
using namespace std;
#define pii pair<int, int>
#define mkpr make_pair
typedef long long ll;
const int N = 2e5 + 10;
const int mod = 998244353;
vector<pii> g[N];
int n;
int dp[N][3], f1[N], f2[N];
int tmp[N];
void dfs(int x, int fa)
{
int cnt = 0, mid = 0;
sort(g[x].begin(), g[x].end());
for (int i = 0; i < g[x].size(); i++)
{
int to = g[x][i].second;
if (to != fa)
dfs(to, x);
}
for (int i = 0; i < g[x].size(); i++)
{
int to = g[x][i].second;
if (to == fa)
mid = cnt;
else
tmp[++cnt] = to;
}
f1[0] = 1, f2[cnt + 1] = 1;
for (int i = 1; i <= cnt; i++)
{
f1[i] = 1ll * f1[i - 1] * (dp[tmp[i]][0] + dp[tmp[i]][1]) % mod;
}
for (int i = cnt; i >= 1; i--)
{
f2[i] = 1ll * f2[i + 1] * (dp[tmp[i]][0] + dp[tmp[i]][2]) % mod;
}
for (int i = 1; i <= mid; i++) //选择删除儿子边中之前的边,使x失效
dp[x][0] = (dp[x][0] + 1ll * f1[i - 1] * dp[tmp[i]][2] % mod * f2[i + 1] % mod) % mod;
dp[x][1] = 1ll * f1[mid] * f2[mid + 1] % mod; //选择删除父亲这条边
dp[x][2] = f1[cnt]; //永远不使x失效
for (int i = mid + 1; i <= cnt; i++) //选择删除儿子边之后的边,使x失效
dp[x][2] = (dp[x][2] + 1ll * f1[i - 1] * dp[tmp[i]][2] % mod * f2[i + 1] % mod) % mod;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(mkpr(i, v));
g[v].push_back(mkpr(i, u));
}

dfs(1, -1);
printf("%d\n", (dp[1][2]) % mod);
}