题解:P14322 「ALFR Round 11」E 空崎ヒナ
Redshift_Shine · · 题解
一道很好的题,只是数据有点差强人意。
特别鸣谢 Iniaugoty 的讨论提供了本题理论可过解的时间复杂度,一定程度上帮助我解出了本题。
首先,我们不难将题目中给出的
所以,在第一步,我们可以预处理
接下来,考虑这样一个问题:对于一个固定的右端点
我们使用树状数组维护每个左端点的答案,而对于最大值发生变化的位置,我们可以使用一个单调栈进行维护。接着,对于每个
这是个非常优美且直接的写法。你将它完成实现并上交,发现你 AC 了这道题。它跑得很快,挤进了最优解的第一页。
但不要急。仔细思考一下,我们不难构造一个
#include <iostream>
using namespace std;
const int N = 1e6;
int main()
{
cout << N << ' ' << N << ' ' << 1 << '\n';
for (int i = 0; i < N; i++)
cout << N - i << " \n"[i == N - 1];
for (int i = 0; i < N; i++)
cout << i << " \n"[i == N - 1];
for (int i = 0; i < N; i++)
cout << i << " \n"[i == N - 1];
for (int i = 0; i < N; i++)
cout << i << " \n"[i == N - 1];
}
在这种情况下,按照上面的算法,你的程序将会在每个
在这惊诧之下,你决定寻找更优的解。
思考一下我们刚才的过程。单调栈内的元素数量可能很大,每次枚举单调栈内的所有元素显然是不现实的。但我们要想起我们在一开始花费
对于一个数
其中,每个
对于每个质数
显然,
但此时,你仍然感觉哪里不对。
根据上面的定义,我们不难发现构建一个尽量小的有很多不同因数的数的最高效方法是选取不同的最小质数乘起来。
使用计算器,我们用这种方法构建一个有很多不同因数的不大于
这个数有
随后,我们随机生成一个
而询问,对于每个右端点
在此种情况下,我们对计算量进行一个不算严谨的估计:
一次树状数组操作的时间复杂度是
在(一个足够后的)右端点
接下来,对于
树状数组有较小的常数,所以上面的估计可能稍大了一点。在本题 2s 的时限下,通过本题已经不成问题。
但你仍不满足。你注意到了另一件事情:在上面的构造中,树状数组上进行了
为了解决这个问题,你想到了一个东西,一个可以被用到莫队里面,用来替换树状数组以更好地平衡左右端点移动与查询的时间复杂度的结构:分块差分数组。
分块差分数组由
而关键就在这里。该结构的修改复杂度远小于查询复杂度,这刚好和我们的需求相吻合。
现在,我们终于可以确定我们的时间复杂度了。
对于
在这样的计算量下,我们终于可以有信心地认为任何正常的评测系统都不可能在这种计算量量级下返回时间超限的结果。
#include <algorithm>
#include <cmath>
#include <iostream>
#include <string>
using namespace std;
const int N = 1e6 + 10, K = 1e2 + 10;
int n, m, k, a[N], b[N], l[N], mx, blks1, blks2, blk1[N], blk2[N], stk[N], sidx, stkd[N], res[N];
int sm[N], sm1[K * K], sm2[K];
basic_string<int> qry[N], fac[N];
void update(int x, int v)
{
sm[x] += v;
sm1[blk1[x]] += v;
sm2[blk2[x]] += v;
}
int query(int x)
{
int res = 0;
while (x and blk1[x] == blk1[x + 1])
res += sm[x], x--;
while (x and blk2[x] == blk2[x + 1])
res += sm1[blk1[x]], x -= blks1;
while (x)
res += sm2[blk2[x]], x -= blks2;
return res;
}
int main()
{
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin >> n >> m >> k;
blks1 = cbrtl(n);
blks2 = blks1 * blks1;
for (int i = 1, j = 0, k = 0; i <= n; i++, j = (j + 1 == blks1 ? 0 : j + 1), k = (k + 1 == blks2 ? 0 : k + 1))
cin >> a[i], blk1[i] = blk1[i - 1] + !j, blk2[i] = blk2[i - 1] + !k;
for (int i = 1; i <= n; i++)
cin >> b[i], mx = max(mx, b[i]);
mx = max(mx, k);
for (int i = 1; i <= mx; i++)
{
for (int j = i; j <= mx; j += i)
fac[j] += i;
}
for (int i = 1; i <= m; i++)
cin >> l[i];
for (int i = 1, r; i <= m; i++)
{
cin >> r;
qry[r] += i;
}
for (int i = 1; i <= n; i++)
{
while (sidx and a[stk[sidx]] <= a[i])
{
stkd[a[stk[sidx]]] = 0;
sidx--;
}
stk[++sidx] = i;
stkd[a[i]] = sidx;
if (b[i] != k)
{
b[i] = abs(b[i] - k);
for (auto &j : fac[b[i]])
{
if (!stkd[j])
continue;
update(stk[stkd[j] - 1] + 1, 1);
update(stk[stkd[j]] + 1, -1);
}
}
else
{
update(1, 1);
update(i + 1, -1);
}
for (auto &j : qry[i])
res[j] = query(l[j]);
}
for (int i = 1; i <= m; i++)
cout << res[i] << " \n"[i == m];
}