给出kk,请你构造一个结点数不超过 100100 的无向图,使得这个无向图中生成树的个数对998244353998244353取模后恰好等于kk

k[0,998244353)k\in [0, 998244353)

UOJ75

Solution

很妙的一道题啊

首先需要知道这么一个结论:

把生成树个数为a1,a2...ana_1,a_2...a_nnn个图通过相邻两个图任意两个点连一条边的方法串联起来后,得到新图的生成树个数为i=1nai\prod_{i=1}^{n}a_i

这个正确性很显然

接着开始乱搞

那么我们考虑总共随机出100010001212个点的图,并用Matrix-Tree定理分别求出它们的生成树个数。每张图每条边生成的概率为0.80.8

然后我们试图找出四张图,使得这四张图的生成树个数的乘积为KK

具体实现的话,显然可以meet in middle,求出任意两张图的生成树个数的乘积和逆元,hash判断一下是否存在

这个算法的正确概率大于99%99\%具体证明见此

感性理解一下(以下把模数视作10910^9

因为每条边选择的概率比较大,且1212个点的完全图生成树个数为121012^{10}远大于模数10910^9,所以生成树个数也会比较大,因此可以近似地看成是模数范围内均匀分布的随机数,这一点与hash类似

所以我们就有了10004=10121000^4=10^{12}个在10910^9范围内均匀分布的随机数,用这101210^{12}个数去覆盖0010910^9中间的所有数概率还是非常大的

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
#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 Mod = 998244353;
const int Maxn = 1e3 + 100;
const int Maxk = 100 + 5;

int N = 1000, K;

inline int Rand (int l, int r) { return rand() % (r - l + 1) + l; }
inline int Rand (int r) { return rand() % r + 1; }

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;
}

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

namespace Matrix
{

const int Maxn = 12 + 5;

int A[Maxn][Maxn];

inline void init () { memset(A, 0, sizeof A); }

inline void add_edge (int x, int y) { --A[x][y], --A[y][x], ++A[x][x], ++A[y][y]; }

inline int gauss (int n)
{
int ans = 1;
for (int i =1; i <= n; ++i)
{
if (!A[i][i])
{
for (int j = i + 1; j <= n; ++j)
if (A[j][i])
{
swap(A[i], A[j]);
ans = (LL)ans * (Mod - 1) % Mod;
break;
}
if (!ans) return 0;
}

int inv = Pow(A[i][i], Mod - 2);
for (int j = i + 1; j <= n; ++j)
{
int now = (LL)A[j][i] * inv % Mod;
for (int k = i; k <= n; ++k) Add (A[j][k], Mod - (LL)A[i][k] * now % Mod);
}
}

for (int i = 1; i <= n; ++i) ans = (LL)ans * A[i][i] % Mod;

return ans;
}
}

struct info
{
int G[Maxk][Maxk], sum1, sum2, n;

inline void init () { memset(G, 0, sizeof G); n = 0; }

inline void build ()
{
n = 12;
Matrix :: init();
for (int i = 1; i < 12; ++i)
for (int j = i + 1; j <= 12; ++j)
{
if (Rand(1, 10) <= 8) G[i][j] = 1, Matrix :: add_edge (i, j);
G[j][i] = G[i][j];
}
sum1 = Matrix :: gauss (n - 1);
sum2 = Pow(sum1, Mod - 2);
}

inline void print ()
{
int m = 0;
for (int i = 1; i < n; ++i)
for (int j = i + 1; j <= n; ++j)
if (G[i][j]) ++m;

printf("%d %d\n", n, m);
for (int i = 1; i < n; ++i)
for (int j = i + 1; j <= n; ++j)
if (G[i][j]) printf("%d %d\n", i, j);
}

} A[Maxn];

#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>

const int Maxs = Maxn * Maxn;

pair <int, pii> Map1[Maxs];
__gnu_pbds :: gp_hash_table <int, pii > Map2;

int state_cnt;

inline void merge (info &x, info y)
{
for (int i = 1; i <= y.n; ++i)
for (int j = 1; j <= y.n; ++j)
x.G[x.n + i][x.n + j] = y.G[i][j];

x.G[x.n][x.n + 1] = x.G[x.n + 1][x.n] = 1;
x.n += y.n;
}

inline void Print (int a, int b, int c, int d)
{
info ans; ans.init();

merge (ans, A[a]); merge (ans, A[b]);
merge (ans, A[c]); merge (ans, A[d]);

ans.print();
}

inline void Solve ()
{
if (!K) { puts("2 0"); return ; }

for (int i = 1; i <= state_cnt; ++i)
{
int x = Map1[i].y.x, y = Map1[i].y.y, val1 = Map1[i].x;
int val2 = (LL)K * val1 % Mod;
if (Map2[val2].x)
{
// printf("%d %d %d %d\n", A[x].sum1, A[y].sum1, A[Map2[val2].x].sum1, A[Map2[val2].y].sum1);
// printf("%lld\n", (LL)A[x].sum1 * A[y].sum1 % Mod * A[Map2[val2].x].sum1 % Mod * A[Map2[val2].y].sum1 % Mod);
Print(x, y, Map2[val2].x, Map2[val2].y);
return ;
}
}
}

inline void Input ()
{
K = read<int>();
}


inline void Init ()
{
srand(time(0));

for (int i = 1; i <= N; ++i) A[i].build ();

for (int i = 1; i <= N; ++i)
for (int j = i; j <= N; ++j)
{
Map1[++state_cnt] = mp((LL)A[i].sum2 * A[j].sum2 % Mod, mp(i, j));
Map2[(LL)A[i].sum1 * A[j].sum1 % Mod] = mp(i, j);
}
}

int main()
{

#ifndef ONLINE_JUDGE
freopen("moonfly.in", "r", stdin);
freopen("moonfly.out", "w", stdout);
#endif

Init();
int Testcase = read<int>();

while (Testcase--)
{
Input();
Solve();
}

return 0;
}