Luogu P4901 Solution / 普及算法大杂烩二回目
闲话
众所周知,前有 P3684=前缀和+二分+最小生成树+并查集+启发式合并,后有 P4901=素数线性筛+询问离线+线段树+斐波那契数列。
两题共同点在于,都是普及算法的大杂烩。讽刺的是,它们竟然都是紫题。
题解
本题可以被拆分成两个互不关联的子问题:
- 对 \([1,\max N_i]\) 区间中的每个整数求出其质因子个数。注意,此处是质因子个数而不是种数,后者需要去重,而本题不需要。该部分可以在 \(O(\max N_i)\) 时间复杂度内完成。
- 对于 \([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');
}
}