11294799 小淘的完美序列
题目链接: https://www.nowcoder.com/practice/7c391af0215a4a7595eb3dd3d9b7f107
支持语言: C++, Java
---
题意
给定正整数 和
,构造一个长度为
的完美序列:序列中每个元素均为
的非零子掩码(即每个元素
满足
且
),所有元素互不相同,且所有元素按升序排列,全部元素按位或等于
。
若无法构造则输出 NO,否则输出 YES 及序列。
---
分析
可行性判断
的非零子掩码数量恰好为
,其中
(
的二进制中
的个数)。
- 若
:不可能找到
个不同的正子掩码,输出
NO。 - 否则:输出
YES。
构造方法
输出 的第
小正子掩码,最后一个元素输出
本身。
这样构造保证:
- 所有元素均为
的正子掩码。
- 全部元素按位或
(因为最后一个元素就是
)。
- 所有元素互不相同(前
个下标互不相同,
是最大子掩码不在前
中,因为
)。
- 序列升序(见下方证明)。
高效生成第 小子掩码
设 的二进制中
所在位置(从低到高)依次为
,对应的位值为
。
则 的第
小正子掩码(
)可由以下方法得到:
将 写成二进制,若
的第
位(从低位数)为
,则贡献
:
$$
验证:以 (位值
)为例:
| 二进制 | ||
|---|---|---|
| 1 | 001 | 1 |
| 2 | 010 | 2 |
| 3 | 011 | 3 |
| 4 | 100 | 4 |
| 5 | 101 | 5 |
| 6 | 110 | 6 |
| 7 | 111 | 7 |
映射保序性证明
对任意 ,有
。
设 与
最高不同位为第
位。由于
,
的第
位为
,贡献
。而
在第
位以下各位的贡献之和满足:
$$
(因为 ,最坏情况下
,总和
。)
因此 映射是严格单调递增的,直接按
输出即为升序序列。
时间复杂度: ,其中
。
---
代码
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n, m;
cin >> n >> m;
vector<long long> bits;
for (int i = 0; i < 44; i++) {
if (m & (1LL << i)) {
bits.push_back(1LL << i);
}
}
int k = bits.size();
long long total_submasks = (k >= 40) ? (long long)9e18 : (1LL << k) - 1;
if (n > total_submasks) {
cout << "NO\n";
return 0;
}
cout << "YES\n";
auto jth_submask = [&](long long j) -> long long {
long long val = 0;
int idx = 0;
while (j > 0) {
if (j & 1) val |= bits[idx];
j >>= 1;
idx++;
}
return val;
};
for (long long j = 1; j <= n - 1; j++) {
cout << jth_submask(j) << ' ';
}
cout << m << '\n';
return 0;
}
Java
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
long n = Long.parseLong(st.nextToken());
long m = Long.parseLong(st.nextToken());
long[] bits = new long[44];
int k = 0;
for (int i = 0; i < 44; i++) {
if ((m & (1L << i)) != 0) {
bits[k++] = 1L << i;
}
}
long totalSubmasks = (k >= 40) ? Long.MAX_VALUE : (1L << k) - 1;
if (n > totalSubmasks) {
System.out.println("NO");
return;
}
StringBuilder sb = new StringBuilder();
sb.append("YES\n");
for (long j = 1; j <= n - 1; j++) {
sb.append(jthSubmask(j, bits));
sb.append(' ');
if (j % 100000 == 0) {
System.out.print(sb);
sb.setLength(0);
}
}
sb.append(m).append('\n');
System.out.print(sb);
}
static long jthSubmask(long j, long[] bits) {
long val = 0;
int idx = 0;
while (j > 0) {
if ((j & 1) != 0) val |= bits[idx];
j >>= 1;
idx++;
}
return val;
}
}
---
关键点
- 可行性: 非零子掩码数量
,
不超过此值则有解。
- 构造: 前
个元素取最小的
个子掩码,最后输出
,保证覆盖所有位。
- 枚举: 第
小子掩码
将
的二进制位"展开"到
的各位位置,
时间直接计算,无需排序或优先队列。
- 单调性: 展开映射严格保序,故输出天然有序。

京公网安备 11010502036488号