NOIP模拟四

NOIP模拟四

number

题目描述

现有 \(2^n\) 个点,点编号为 \(0\sim2^n-1\)

定义这些点的一张异或图为:

先选定一个集合 \(S\)

对于原图上编号为 \(x\) 和编号为 \(y\) 的点,如果 \(x\oplus y\in S\),那么在 \(x\)\(y\) 之间连一条无向边。

数出有多少个集合 \(S\),满足它的大小尽量小且异或图为连通图。答案对 \(998244353\) 取模。

输入格式

请从 number.in 中读入数据。

输入一行一个数 \(n\)

输出格式

请将答案输出到 number.out 中。

输出一行,表示答案。

样例

Input 1

1

Output 1

1

Input 2

2

Output 2

3

Input 3

3

Output 3

28

Input 4

189

Output 4

952004352

Input 5

999876

Output 5

936053454

数据范围

【数据范围】

对于所有数据,保证:\(n\le 10^6\)

测试点编号 \(n \le\)
\(1\sim 3\) \(5\)
\(4 \sim 6\) \(200\)
\(7 \sim 10\) \(10^{6}\)

样例解释

对于第二个样例,集合 \(S\) 的大小最小是 \(2\)\(\{1,2\}\)\(\{1,3\}\)\(\{2,3\}\) 均满足条件。

转换问题:对于一个 \(x\),如果有一个 \(a\in S\),那么 \(x\) 就向 \(x\oplus a\) 连边。


我们发现,集合 \(S\) 中每个数线性无关(即不存在某个数是其余若干个数的异或和)。

证明:如果有一个 \(a\in S\),那么 \(x\) 就向 \(x\oplus a\) 连边。

那么假如又有一个 \(b\in S\),则 \(x\) 会向 \(x\oplus a\) 连边,\(x\oplus a\) 会向 \(x\oplus a\oplus b\) 连边。

这时如果有 \(a\oplus b\in S\),则 \(x\) 会向 \(x\oplus a\oplus b\) 连边,我们会发现这不是必须的,因为此时通过 \(a\)\(b\) 已经让它们连边了,就不需要再有一个 \(a\oplus b\in S\) 了。 可以把 \(a\oplus b\) 删掉。

也就是说,如果一个集合 \(S\) 如果其中某个数是其余若干个数的异或和,则这个集合不是最短的,因为可以把这个数删掉。


我们又发现,集合 \(S\) 最短长度为 \(n\)

证明:因为集合 \(S\) 中每个数线性无关,所以任选任意个数异或和都不一样,共有 \(2^n\) 种选法,去掉不选即可以组成 \(2^n-1\) 种数。

若对于任意的 \(a\in S\),有 \(a<2^n\),那么集合任选任意个数异或和都小于 \(2^n\),和上面一结合,得出集合 \(S\) 任选任意个数异或和可以组成 \(1\sim2^n-1\) 的数。

也就是以编号为 \(0\) 的节点出发,可以通过 \(S\) 的任意组合链接到 \(1\sim2^n-1\) 的节点。

可以反证当 \(S\) 长度为 \(n-1\) 时无解。


那么我们可以做一个 dp 来求这个方案数。设 \(f_i\) 为决定了集合 \(S\)\(i-1\) 个数,要求第 \(i\) 个数的方案数。第 \(i\) 位可以选 \(2^n\) 种数,因为前面 \(i-1\) 位选过了的若干个异或和不能选,所以去掉 \(2^{i-1}\) 种方案。转移方程为:

\[f_i=f_{i-1}\times(2^n-2^{i-1}) \]

因为集合 \(S\) 无序,所以最后要除以 \(n!\)

/**
* @file number.cpp
* @tag: #数学
* @author: ZnPdCo
* @date: 2023-12-28 12:54:00
* @problem: https://www.xinyoudui.com/contest?courses=685&books=676&pages=19971&fragments=63901&problemId=19061
**/
#include <cstdio>
#define ll long long
#define N 1000010
#define P 998244353
ll n;
ll ans = 1;
ll qpow(ll x, ll y) {
if(y == 0) return 1;
if(y % 2 == 1) return x * qpow(x, y-1) % P;
ll tmp = qpow(x, y/2);
return tmp * tmp % P;
}
ll npow, xpow = 1, nfact = 1;
int main() {
freopen("number.in", "r", stdin);
freopen("number.out", "w", stdout);
scanf("%lld", &n);
npow = qpow(2, n);
for(ll i = 1; i <= n; i++) {
(ans *= ((npow - xpow)%P+P)%P) %= P;
(xpow *= 2) %= P;
(nfact *= i) %= P;
}
(ans *= qpow(nfact, P-2)) %= P;
printf("%lld", ans);
}
posted @   ZnPdCo  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示