题解:P5539 【XR-3】Unknown Mother-Goose
前言
世界があたしを拒んでも,
今、愛の唄 歌わせてくれないかな?
思路
首先有一个显然的暴力,设集合
虽然这样是可行的,但这个方法显然最坏是
不过如果你会 bitset 的话,显然能想到这个方法可以使用 bitset 优化空间。bitset 的原理就是对一个整型变量,将其的每一个二进制位都当作一个 bool 来使用。使用 bitset 处理的空间复杂度为
但是这样仍然无法通过,因为如果
- 当
a_i<w 时,我们很容易可以知道a_i 倍数在 bitset 中的循环节为a_i 个一循环,那我们只需要在O(w) 的复杂度内造出a_i 的循环节,再用最坏O(\frac{n}{w}) 的复杂度把循环节和用于标记的 bitset 或起来就好了。 - 当
a_i\ge w 时,我们只需要暴力枚举a_i 倍数的位置即可。最坏时间复杂度是O(\frac{n}{w}) 。
输出时,分类为全部在同一个整型内,一个在左边的整型而两个在右边的整型,两个在左边的整型而一个在右边的整型这三种情况解决即可。具体可以看代码的输出部分。
最后总时间复杂度最坏为
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
unsigned long long p[16500005], h[70];
int n, s, u, m, ans;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> s;
m = n >> 6;//bitset 的整型数量
while (s--) {
cin >> u;
if (u < 64) {//处理 < 64 的部分
for (int i = 0; i < (u << 6); i += u) {
h[i >> 6] |= 1ull << (i & 63);//这里相当于 %64,但是 &63 比 %64 快不少
}
for (int i = 0; i <= m + 1; i += u) for (int j = 0; j < u; j++)p[i + j] |= h[j];
for (int i = u - 1; i >= 0; i--) {
h[i] = 0;
}
} else {//处理 >= 64 的部分
for (int i = 0; i <= n; i += u) {
p[i >> 6] |= 1ull << (i & 63);
}
}
}
p[0] &= p[0] - 1;//去掉第0位
if ((n & 63) != 63) p[m] &= (1ull << ((n & 63) + 1)) - 1;//去掉第n位以外的位
for (int i = 0; i <= m; i++) ans += __builtin_popcountll((p[i]) & (p[i] >> 1) & (p[i] >> 2));//在同一整型内
for (int i = 1; i <= m; i++) ans += __builtin_popcountll((p[i]) & (p[i - 1] >> 62) & ((p[i] << 1) | p[i - 1] >> 63));//不在同一整型内
// __builtin_popcountll() 可以在 O(1) 的时间复杂度内求出一个 long long 的二进制里 1 的数量
cout << ans;
return 0;
}