Luogu P4901 Solution / 普及算法大杂烩二回目

闲话

众所周知,前有 P3684=前缀和+二分+最小生成树+并查集+启发式合并,后有 P4901=素数线性筛+询问离线+线段树+斐波那契数列。

两题共同点在于,都是普及算法的大杂烩。讽刺的是,它们竟然都是紫题。

题解

本题可以被拆分成两个互不关联的子问题:

  1. \([1,\max N_i]\) 区间中的每个整数求出其质因子个数。注意,此处是质因子个数而不是种数,后者需要去重,而本题不需要。该部分可以在 \(O(\max N_i)\) 时间复杂度内完成。
  2. 对于 \([1,\max N_i]\) 区间中的每个整数求出其从属的队伍编号。目前作者仍没有发现该部分不带 \(\log\) 的做法,所以该部分可以在 \(O(\max N_i \log \max K_i)\) 的时间复杂度内完成。

Part \(1\)

该部分非常简单。注意到若将素数线性筛的标记 vis 数组过程看作建树,那么 \(x\) 的父亲是 \(x\) 除以 \(x\) 的最小质因子。也就是说,\(x\) 的质因子个数恰好比其父亲多一个。由此,可以通过对素数线性筛的代码进行极其细微的修改达到这个目的。

以下代码中,\(\text{ds}_x\)\(x\) 的质因子个数。

for (int i = 2; i <= mxp; i++)
{
    if (!vis[i])
        pr[++idx] = i, ds[i] = 1;
    for (int j = 1; i * pr[j] <= mxp; j++)
    {
        vis[i * pr[j]] = true;
        ds[i * pr[j]] = ds[i] + 1;
        if (i % pr[j] == 0)
            break;
    }
}

Part \(2\)

本题的难点为这个部分。将题面进行一些转换,考虑加入一个数后每一个小队“选人”的策略会发生如何的改变。

首先,每个小队会选择目前队伍中第 \(1,2,3,5,8,\dots\) 个人。如果认为他们会在选中每个人之后选择接下来的第 \(1,1,2,3,5,8,\dots\) 个人,那么会变得更好处理。

具体来说,我们使用一个线段树,每个节点中维护区间内等待人数最小值以及其位置。显然,这个人将会加入目前编号最小的不需要额外跳过人的小队。我们按照斐波那契数列的值更新对应小队接下来选择的人的位置。

然而这样讲会有点抽象。还可以这样理解:

这个将要被加进来的人会被按编号顺序依次拉到每个小队进行检查。如果某个小队决定收入接下来遇到的第一个人,那么将他拉进来。否则,将决定从“收入接下来遇到的第 \(x\) 个人”改为“收入接下来遇到的第 \((x-1)\) 个人,然后将他送到下一个小队检查。

这种方法可以方便地在线段树上维护。

Part Ex

对于任何高 I/O 量的题目(比如这题),加快读快写都会给程序带来巨大的运行效率提升。

同时在低数据范围下,有一些不需要做的计算不要做,否则会浪费时间。

至此,本题解决。

代码

#include <algorithm>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <tuple>
#include <utility>
using namespace std;
const int N = 5e6 + 10, K = 1e4 + 10, M = 1e2 + 10, T = 1e6 + 10;
using pii = pair<int, int>;
using t3i = tuple<int, int, int>;
int n, res[T], pr[N], ds[N], cur[K], mxp, k, buc[M], idx, tidx = 1;
bitset<N> vis;
t3i qry[T];
template <typename _Tp> void read(_Tp &x)
{
    char ch;
    while (ch = getchar(), !isdigit(ch))
        ;
    x = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
    }
}
template <typename _Tp, typename... _Args> void read(_Tp &x, _Args &...args)
{
    read(x);
    read(args...);
}
template <typename _Tp> void print(_Tp x)
{
    if (x < 0)
        return (putchar('-'), print(-x));
    if (x > 9)
        print(x / 10);
    putchar((x % 10) ^ 48);
}
struct st
{
    int mnv, mnp, tag;
    short lvl;
    st operator+(const st &x) const
    {
        return {min(mnv, x.mnv), mnv != x.mnv ? (mnv < x.mnv ? mnp : x.mnp) : (mnp < x.mnp ? mnp : x.mnp), 0, 0};
    }
} tr[K << 2];
void build(int x, int l, int r)
{
    if (l == r)
    {
        tr[x] = {0, l, 0, 0};
        return;
    }
    int mid = (l + r) >> 1;
    build(x << 1, l, mid);
    build(x << 1 | 1, mid + 1, r);
    tr[x] = tr[x << 1] + tr[x << 1 | 1];
}
void psh(int x)
{
    tr[x << 1].mnv += tr[x].tag;
    tr[x << 1 | 1].mnv += tr[x].tag;
    tr[x << 1].tag += tr[x].tag;
    tr[x << 1 | 1].tag += tr[x].tag;
    tr[x].tag = 0;
}
void update(int x, int l, int r, int lb, int rb, int v)
{
    if (l >= lb and r <= rb)
    {
        tr[x].tag += v;
        tr[x].mnv += v;
        return;
    }
    if (tr[x].tag)
        psh(x);
    int mid = (l + r) >> 1;
    if (lb <= mid)
        update(x << 1, l, mid, lb, rb, v);
    if (rb > mid)
        update(x << 1 | 1, mid + 1, r, lb, rb, v);
    tr[x] = tr[x << 1] + tr[x << 1 | 1];
}
void updatesg(int x, int l, int r, int tar)
{
    if (l == r)
    {
        tr[x] = {buc[tr[x].lvl], l, 0, static_cast<short>(tr[x].lvl + 1)};
        return;
    }
    if (tr[x].tag)
        psh(x);
    int mid = (l + r) >> 1;
    if (tar <= mid)
        updatesg(x << 1, l, mid, tar);
    else
        updatesg(x << 1 | 1, mid + 1, r, tar);
    tr[x] = tr[x << 1] + tr[x << 1 | 1];
}
int main()
{
    read(n);
    for (int i = 1, x, y; i <= n; i++)
    {
        read(x, y);
        mxp = max(mxp, x);
        qry[i] = {x, y, i};
    }
    sort(qry + 1, qry + n + 1);
    buc[0] = buc[1] = 1;
    for (int i = 2; buc[i - 1] + buc[i - 2] <= mxp; i++)
    {
        buc[i] = buc[i - 1] + buc[i - 2];
    }
    k = min(mxp, K - 10);
    build(1, 1, k);
    for (int i = 2; i <= mxp; i++)
    {
        if (!vis[i])
            pr[++idx] = i, ds[i] = 1;
        for (int j = 1; i * pr[j] <= mxp; j++)
        {
            vis[i * pr[j]] = true;
            ds[i * pr[j]] = ds[i] + 1;
            if (i % pr[j] == 0)
                break;
        }
    }
    while (tidx <= n and get<0>(qry[tidx]) == 1)
    {
        res[get<2>(qry[tidx])] = (get<1>(qry[tidx]) == 1 ? 0 : -1);
        tidx++;
    }
    for (int i = 1, tps; i <= mxp; i++)
    {
        tps = tr[1].mnp;
        if (tr[1].mnv)
        {
            update(1, 1, k, 1, k, -1);
        }
        else
        {
            updatesg(1, 1, k, tps);
            update(1, 1, k, 1, tps, -1);
            cur[tps] += ds[i];
        }
        while (tidx <= n and get<0>(qry[tidx]) == i)
        {
            auto &[tx, ty, tz] = qry[tidx];
            res[tz] = cur[ty];
            if (!res[tz])
                res[tz] = -1;
            tidx++;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        print(res[i]);
        putchar('\n');
    }
}
posted @ 2024-10-05 00:34  丝羽绫华  阅读(2)  评论(0编辑  收藏  举报