Codeforces Round #774 (Div. 2)E题题解

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(1n,m106)的表格,第 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} 2log2n。故这个新的表格的第 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×xlogxn+1min,毕竟当LCM超过了最大的拓展数列长度 m × x ⌊ log ⁡ x n ⌋ m \times x^{\lfloor\log _x n\rfloor} m×xlogxn则就没有共同出现的数字了,那么这个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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值