Codeforces Round #774 (Div. 2)
E. Power Board
题目陈述
有一个 n × m ( 1 ≤ n , m ≤ 1 0 6 ) n\times m(1\le n,m\le10^6) n×m(1≤n,m≤106)的表格,第 i i i行 j j j列的数为 i j i^j ij,求表格中不同的数的个数 r e s res res
输入格式
一行包含两个整数 n , m n,m n,m
输出格式
表格中不同的数的个数 r e s res res
- 容易想到,第一行的所有数字都为 1 1 1
- 我们依次考虑,以 2 , 3 , 5 , 7 2,3,5,7 2,3,5,7这样的质数为基底的次方
- 对于 2 2 2而言,我们知道 2 2 2的次方数为 2 , 4 , 8 , 16 , 32 , 64 , ⋯ 2,4,8,16,32,64,\cdots 2,4,8,16,32,64,⋯
- 我们设基底 x = 2 x=2 x=2,我们重新把原来的表格中,行的第一个数为 2 2 2的整次幂的行,抽取出来做成一个表格。新表格的列数都为 m m m,但是其行数是不超过 n n n的最大的 2 2 2的整数次幂,即 2 ⌊ log 2 n ⌋ 2^{\lfloor \log _2n \rfloor} 2⌊log2n⌋。故这个新的表格的第 i i i行的第一列的数即为 2 i 2^i 2i,现在我们来计算新表格中不同的数的个数。
- 我们把新表格第 i i i行拓展为长度为 i × m i\times m i×m的数列,其中在原本行中没有出现的数。打个比方,比如 m = 3 m=3 m=3,我们先忽视 n n n的值,对于第一行有 2 , 4 , 8 2,4,8 2,4,8,数列长度为 m × 1 = m m\times 1=m m×1=m;对于第二行有 2 , 4 , 8 , 16 , 32 , 64 2,4,8,16,32,64 2,4,8,16,32,64,数列长度为 2 m 2m 2m,原本行中的第 i i i列对应数列中的第 2 i 2i 2i项;对于第三行有 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 2,4,8,16,32,64,128,256,512 2,4,8,16,32,64,128,256,512,数列长度为 3 m 3m 3m,原本行中的第 i i i列对应数列中的第 3 i 3i 3i项;以此类推。
- 这样,第一行就对应
1
1
1的倍数,第二行就对应
2
2
2的倍数,第三行就对应
3
3
3的倍数。我们用状态压缩来枚举某些行
a
l
<
a
q
<
⋯
<
a
p
a_l<a_q<\cdots<a_p
al<aq<⋯<ap,计算他们在表格中共同出现的数的数量。则这些行下标的最小公倍数
L
C
M
LCM
LCM,表示在拓展数列中每
L
C
M
LCM
LCM次共同出现一次,但是有的行的拓展数列长度可能不够长,所以我们要取行下标最小的那个行
first_row
,其长度为first_row * m
,其除以LCM
即是枚举的这些行 a l < a q < ⋯ < a p a_l<a_q<\cdots<a_p al<aq<⋯<ap在表格中共同出现的数的数量。存在边界条件LCM
过大的情况,故需要跟 m × x ⌊ log x n ⌋ + 1 m\times x^{\lfloor\log _x n\rfloor}+1 m×x⌊logxn⌋+1取min
,毕竟当LCM
超过了最大的拓展数列长度 m × x ⌊ log x n ⌋ m \times x^{\lfloor\log _x n\rfloor} m×x⌊logxn⌋则就没有共同出现的数字了,那么这个LCM
就没有意义了,也是为了防止LCM
太大爆long long
。 - 上面计算的共同出现的数的数量,就相当于容斥原理中集合与集合的交集,根据容斥原理的奇加偶减原则,便容易写出程序(代码参考
jiangly
)。
#include <bits/stdc++.h>
#include <bits/extc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
#define debug(x) cerr << #x << ": " << x << '\n';
#define bd cerr << "----------------------" << el;
#define el '\n'
#define cl putchar('\n');
#define pb push_back
#define eb emplace_back
#define x first
#define y second
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define lop(i, a, b) for (int i = (a); i < (b); i++)
#define dwn(i, a, b) for (int i = (a); i >= (b); i--)
#define ceil(a, b) (a + (b - 1)) / b
#define ms(a, x) memset(a, x, sizeof(a))
#define INF 0x3f3f3f3f
#define db double
#define all(x) x.begin(), x.end()
#define reps(i, x) for (int i = 0; i < x.size(); i++)
#define cmax(a, b) a = max(a, b)
#define cmin(a, b) a = min(a, b)
#define mpa make_pair
typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<db, db> PDD;
typedef vector<int> vci;
typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> Set;
typedef tree<PII, null_type, less<PII>, rb_tree_tag, tree_order_statistics_node_update> Tree;
struct read
{
static const int M = 1 << 21;
char buf[M], *S = buf, *P = buf, c, f;
inline char getc()
{
return (S == P && (P = (S = buf) + fread(buf, 1, 1 << 21, stdin), S == P) ? EOF : *S++);
}
template <typename T>
read &operator>>(T &x)
{
for (c = 0; !isdigit(c); c = getc())
f = c;
for (x = 0; isdigit(c); c = getc())
x = x * 10 + (c - '0');
return x = (f ^ '-') ? x : -x, *this;
}
} fin;
constexpr int N = 1e5 + 10, M = 2e6 + 10, B = 66, md = 1e9 + 7;
const double PI = acos(-1), eps = 1e-8;
int T, n, m;
int main()
{
fin >> n >> m;
LL res = 1; //计算1
vector<bool> flag(n + 1, false);
rep(i, 2, n)
{
if(flag[i])
continue;
LL x = i;
int lg = 1;
while(x * i <= n)
{
lg ++ ;
x *= i;
flag[x] = true;
}
//以x为基底
//长度分别为m, 2m, 3m
lop(s, 1, 1 << lg)
{//状态压缩枚举
LL LCM = 1;
int first_row = -1;
lop(i, 0, lg)
{//第i位表示 x ^ (i + 1)
if(s >> i & 1)
{
if(first_row == -1)
first_row = i + 1;
LCM = min(lg * m + 1LL, lcm(LCM, i + 1LL));
// min(最长的那一段m, 所有指数的lcm)
//lg * m + 1LL是为了防爆LL
}
}
res += (__builtin_parity(s) ? 1 : -1) * (first_row * m / LCM);
//容斥,奇加偶减
//第一段的长度m * first_row
}
}
cout << res << el;
}