给你一张nn个点mm条边的图,每次会随机选择一个还未删除的点 vv,然后访问所有与 vv 连通的点,然后删除点 vv ,直至所有点都被删除。

求期望访问次数,答案对998244353998244353取模

n105,m[n1,n]n\le 10^5, m\in[n - 1, n]

FZOJ 192

Solution

Tree

先考虑树的情况

显然这个期望就是每种情况的概率和,又因为期望具有线性性,所以可以考虑两两点对对答案的贡献

有序点对(x,y)(x, y),树上路径长度为cc(经过cc个节点),对答案的贡献为1c\frac{1}{c}

若在访问xx时,yy能够被访问,则说明xxyy的这条链还没有断

也就是说,在所有删点方案中,只要xxx到y这条链除了x之外的任意一个点被删除之前删除,就会产生贡献

这个概率就是在只考虑x到y这条链的任意排列方案时,钦定第一个为xx的概率,即为1c\frac{1}{c}

所以我们只需要对每个cc求出长度为cc的路径条数

点分治 + NTT合并即可

这个用NTT合并的trick也不难理解

在点分治合并答案时,下标(其实就是深度)也是一个形如i+j=ki+j=k的合并方式,且是乘法运算,所以可以用NTT优化

光以上的部分我就调了一年。。。


Base-Ring Tree

接下来考虑基环树,仍然是考虑点对(x,y)(x, y)的贡献

  • (x,y)(x, y)在同一棵子树内:与树的计算方法相同

  • (x,y)(x,y)不在同一棵子树内:

    19-3-1-4

    dep[x]+dep[y]=cdep[x] + dep[y] = c,则此时删除xxx,yx, y仍联通的概率等于xxa+ca+c个点中最先删除或在b+cb+c个点中最先删除的概率(分别对应经过环的两侧)

    通过容斥我们可以得到此时贡献为1a+c+1b+c1a+b+c\frac{1}{a + c} + \frac{1}{b + c} - \frac{1}{a + b + c}

    随便选一条边破环为链,用类似CDQ分治的分治方法 + NTT即可

    其实这个分治方法也类似于序列上的点分治,就是每次考虑过中点的贡献

总复杂度O(nlog2n)O(n\log^2 n)

具体实现上,可以先破环为链,对剩下的部分计算一次树情况的答案

这部分我们算出来的答案既包括了每个子树的答案,又包括了各个子树之间通过环上某一条路径的答案

例如,假设破的环在aa部分中,那么我们相当于已经计算了1b+c\frac{1}{b+c}的答案了

那么我们只需要分治计算1a+c\frac{1}{a+c}1a+b+c\frac{1}{a+b+c}的贡献了

这样代码写起来会比较方便,更具体的部分可以直接看代码了,自我感觉写得应该是比较容易理解的

Code

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
#include <bits/stdc++.h>

#define x first
#define y second
#define y1 Y1
#define y2 Y2
#define mp make_pair
#define pb push_back

using namespace std;

typedef long long LL;
typedef pair <int, int> pii;

template <typename T> inline int Chkmax (T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0; }
template <typename T> inline T read ()
{
T sum = 0, fl = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1;
for (; isdigit(ch); ch = getchar()) sum = (sum << 3) + (sum << 1) + ch - '0';
return sum * fl;
}

inline void proc_status ()
{
ifstream t ("/proc/self/status");
cerr << string (istreambuf_iterator <char> (t), istreambuf_iterator <char> ()) << endl;
}

const int Maxn = 1e5 + 100;
const int Mod = 998244353;
const int g = 3;
const int inf = 0x3f3f3f3f;

int N, M;
vector <int> G[Maxn];

namespace MATH
{
inline void Add (int &a, int b) { a += b; if (a >= Mod) a -= Mod; }

inline int Pow (int a, int b)
{
int ans = 1;
for (int i = b; i; i >>= 1, a = (LL)a * a % Mod) if (i & 1) ans = (LL)ans * a % Mod;
return ans;
}
}

using namespace MATH;

namespace Poly
{
int n, rev[Maxn << 2], F[Maxn << 2], G[Maxn << 2], Wn[Maxn << 2], Wn_inv[Maxn << 2];

inline void init (int maxn)
{
for (int i = 1; i <= maxn; i <<= 1) Wn[i] = Pow(g, (Mod - 1) / (i << 1)), Wn_inv[i] = Pow(Wn[i], Mod - 2);
}

inline void dft (int *A, int flag)
{
for (int i = 0; i < n; ++i) if (rev[i] < i) swap (A[i], A[rev[i]]);

for (int mid = 1; mid < n; mid <<= 1)
{
int wn = Wn[mid];
if (flag < 0) wn = Wn_inv[mid];
for (int i = 0; i < n; i += (mid << 1))
{
int W = 1;
for (int j = i; j < i + mid; ++j, W = (LL)W * wn % Mod)
{
int a = A[j], b = (LL)W * A[j + mid] % Mod;
A[j] = (a + b) % Mod, A[j + mid] = (a - b + Mod) % Mod;
}
}
}

int inv = Pow(n, Mod - 2);
if (flag < 0) for (int i = 0; i < n; ++i) A[i] = (LL)A[i] * inv % Mod;
}

inline void mul (int *A, int N, int *B, int M, int *Ans)
{
n = 1; while (n <= N + M) n <<= 1;
for (int i = 0; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) + (i & 1 ? (n >> 1) : 0);
for (int i = 0; i < n; ++i) F[i] = i <= N ? A[i] : 0;
for (int i = 0; i < n; ++i) G[i] = i <= M ? B[i] : 0;

dft (F, 1), dft (G, 1);
for (int i = 0; i < n; ++i) F[i] = (LL)F[i] * G[i] % Mod;
dft (F, -1);
for (int i = 0; i <= N + M; ++i) Ans[i] = F[i];
}
}

int Ans[Maxn];

namespace TREE
{
int Vis[Maxn], size[Maxn];
int root, min_size, now_size;

inline void dfs_pre (int x, int f = 0)
{
size[x] = 1;
for (int y : G[x])
{
if (y == f || Vis[y]) continue;
dfs_pre (y, x);
size[x] += size[y];
}
}

inline void get_root (int x, int f = 0)
{
/**/ int now = now_size - size[x];
for (int y : G[x])
{
if (y == f || Vis[y]) continue;
get_root (y, x);
Chkmax (now, size[y]);
}

if (Chkmin (min_size, now)) root = x;
}

int Sum[Maxn], Buc_All[Maxn], Buc_Now[Maxn], max_dep;

inline void get_dep (int x, int f, int dep = 1)
{
++Buc_Now[dep], Chkmax(max_dep, dep);
for (int y : G[x])
{
if (y == f || Vis[y]) continue;
get_dep (y, x, dep + 1);
}
}

inline void calc (int x)
{
int n = 1; ++Ans[1];

for (int y : G[x])
{
if (Vis[y]) continue;
max_dep = 0, get_dep (y, x);
Sum[1] = Buc_All[1] = 1;

Poly :: mul (Buc_All, n, Buc_Now, max_dep, Sum);

for (int i = 1; i <= n + max_dep; ++i) Add (Ans[i], 2ll * Sum[i] % Mod);

Chkmax(n, max_dep + 1);
for (int i = 2; i <= n; ++i)
Buc_All[i] += Buc_Now[i - 1], Buc_Now[i - 1] = 0;
}

for (int i = 0; i <= n; ++i) Buc_All[i] = Buc_Now[i] = Sum[i] = 0;
}

inline void divide (int x)
{
root = x, min_size = inf;
dfs_pre (x), now_size = size[x], get_root (x), x = root;

calc (x); Vis[x] = 1;

for (int y : G[x]) if (!Vis[y]) divide (y);
}
}

namespace CIRCLE
{
int Vis[Maxn], Stack[Maxn], top;
int Circle[Maxn], len, In[Maxn];

inline void find_circle (int x, int f = 0)
{
if (len) return ;
Vis[x] = 1, Stack[++top] = x;

for (int y : G[x])
{
if (y == f) continue;
if (Vis[y] && !len)
{
while (Stack[top] != y) In[Stack[top]] = 1, Circle[++len] = Stack[top--];
In[y] = 1, Circle[++len] = y;
return ;
}
find_circle (y, x);
}

--top;
}

int n, m;
int L[Maxn], R[Maxn], Sum[Maxn << 2];

inline void get_dep (int x, int f, int dep, int op)
{
if (!op) ++L[dep], Chkmax(n, dep);
else ++R[dep], Chkmax(m, dep);

for (int y : G[x])
{
if (y == f || In[y]) continue;
get_dep (y, x, dep + 1, op);
}
}

inline void divide (int l, int r)
{
if (l == r) return ;

int mid = l + r >> 1;

n = m = 0;
for (int i = l; i <= mid; ++i) get_dep (Circle[i], 0, i, 0);
for (int i = mid + 1; i <= r; ++i) get_dep (Circle[i], 0, len - i + 1, 1);

// l -> 0 ; n -> n - l
// (len - r + 1) -> 0 ; m -> m - (len - r + 1)
/**/ Poly :: mul (L + l, n - l, R + (len - r + 1), m - (len - r + 1), Sum + l + (len - r + 1));

for (int i = l + (len - r + 1); i <= n + m; ++i) Add (Ans[i], 2ll * Sum[i] % Mod);
for (int i = l; i <= n; ++i) L[i] = 0;
for (int i = (len - r + 1); i <= m; ++i) R[i] = 0;

/********************************************************************************/

n = m = 0;
for (int i = l; i <= mid; ++i) get_dep (Circle[i], 0, 0, 0);
for (int i = mid + 1; i <= r; ++i) get_dep (Circle[i], 0, 0, 1);

Poly :: mul (L, n , R, m, Sum);

for (int i = 0; i <= n + m; ++i) Add (Ans[len + i], Mod - 2ll * Sum[i] % Mod);
for (int i = 0; i <= n; ++i) L[i] = 0;
for (int i = 0; i <= m; ++i) R[i] = 0;

divide (l, mid), divide (mid + 1, r);
}

inline void work ()
{
find_circle (1);

int x = Circle[1], y = Circle[len];
G[x].erase (find (G[x].begin(), G[x].end(), y));
G[y].erase (find (G[y].begin(), G[y].end(), x));

/**/ TREE :: divide (1);
divide (1, len);
}
}

inline void Solve ()
{
Poly :: init (N << 1);

if (M == N - 1) TREE :: divide (1);
else CIRCLE :: work ();

int ans = 0;
for (int i = 1; i <= N; ++i) Add (ans, (LL)Ans[i] * Pow(i, Mod - 2) % Mod);

//for (int i = 1; i <= N; ++i) cout << i << ' ' << Ans[i] << endl;

cout << ans << endl;
}

inline void Input ()
{
N = read<int>(), M = read<int>();

for (int i = 1; i <= M; ++i)
{
int x = read<int>(), y = read<int>();
G[x].pb(y), G[y].pb(x);
}
}

int main()
{

freopen("eat.in", "r", stdin);
freopen("eat.out", "w", stdout);

Input ();
Solve ();

return 0;
}