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

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

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(1n,m106)的表格,第iiijjj列的数为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⌊log⁡2n⌋2^{\lfloor \log _2n \rfloor}2log2n。故这个新的表格的第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⌊log⁡xn⌋+1m\times x^{\lfloor\log _x n\rfloor}+1m×xlogxn+1min,毕竟当LCM超过了最大的拓展数列长度m×x⌊log⁡xn⌋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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值