Codeforces Round #774 (Div. 2)
E. Power Board
题目陈述
有一个n×m(1≤n,m≤106)n\times m(1\le n,m\le10^6)n×m(1≤n,m≤106)的表格,第iii行jjj列的数为iji^jij,求表格中不同的数的个数resresres
输入格式
一行包含两个整数n,mn,mn,m
输出格式
表格中不同的数的个数resresres
- 容易想到,第一行的所有数字都为111
- 我们依次考虑,以2,3,5,72,3,5,72,3,5,7这样的质数为基底的次方
- 对于222而言,我们知道222的次方数为2,4,8,16,32,64,⋯2,4,8,16,32,64,\cdots2,4,8,16,32,64,⋯
- 我们设基底x=2x=2x=2,我们重新把原来的表格中,行的第一个数为222的整次幂的行,抽取出来做成一个表格。新表格的列数都为mmm,但是其行数是不超过nnn的最大的222的整数次幂,即2⌊log2n⌋2^{\lfloor \log _2n \rfloor}2⌊log2n⌋。故这个新的表格的第iii行的第一列的数即为2i2^i2i,现在我们来计算新表格中不同的数的个数。
- 我们把新表格第iii行拓展为长度为i×mi\times mi×m的数列,其中在原本行中没有出现的数。打个比方,比如m=3m=3m=3,我们先忽视nnn的值,对于第一行有2,4,82,4,82,4,8,数列长度为m×1=mm\times 1=mm×1=m;对于第二行有2,4,8,16,32,642,4,8,16,32,642,4,8,16,32,64,数列长度为2m2m2m,原本行中的第iii列对应数列中的第2i2i2i项;对于第三行有2,4,8,16,32,64,128,256,5122,4,8,16,32,64,128,256,5122,4,8,16,32,64,128,256,512,数列长度为3m3m3m,原本行中的第iii列对应数列中的第3i3i3i项;以此类推。
- 这样,第一行就对应111的倍数,第二行就对应222的倍数,第三行就对应333的倍数。我们用状态压缩来枚举某些行al<aq<⋯<apa_l<a_q<\cdots<a_pal<aq<⋯<ap,计算他们在表格中共同出现的数的数量。则这些行下标的最小公倍数LCMLCMLCM,表示在拓展数列中每LCMLCMLCM次共同出现一次,但是有的行的拓展数列长度可能不够长,所以我们要取行下标最小的那个行
first_row,其长度为first_row * m,其除以LCM即是枚举的这些行al<aq<⋯<apa_l<a_q<\cdots<a_pal<aq<⋯<ap在表格中共同出现的数的数量。存在边界条件LCM过大的情况,故需要跟m×x⌊logxn⌋+1m\times x^{\lfloor\log _x n\rfloor}+1m×x⌊logxn⌋+1取min,毕竟当LCM超过了最大的拓展数列长度m×x⌊logxn⌋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;
}

本文介绍了如何解决Codeforces Round #774的一道题目,涉及不同质数幂在矩阵中的计数问题,通过状态压缩和容斥原理的方法,计算特定行中不同数的个数。关键步骤包括分析质数次方数的分布,利用状态压缩枚举行的公共特征,并结合容斥原理计算最终结果。

314

被折叠的 条评论
为什么被折叠?



