[SNCPC2024] 2024 年陕西省大学生程序设计 J题猜质数II 题解

题目链接:CF 或者 洛谷

前提说明

其实在上个月就见到这题了,当时很想做这题,结果找不到做题链接,也不知道出处,原来是陕西省赛的捧杯题

个人评价觉得是一道很不错的题,难度适中。

讲解

其实题解写的挺不错的,比很多比赛的题解写的详细许多了。这里站在我的角度分析这题吧:

先观察要求的式子:

score(x,l,r)=i=lrf(x,ai)

其中

f(x,y)={uy,x=1u,1<xy, gcd(x,y)=1xy,x1, gcd(x,y)=x0,otherwise

u 是题目给定查询的。看到这一坨就很麻烦,我们看看询问啥东西:

x=11e6score(x,l,r)=x=11e6i=lrf(x,ai)

那看来肯定是需要变形了,我们重点分析一下:

x=11e6i=lrf(x,ai)=i=lrx=11e6f(x,ai)

x=11e6f(x,ai)=?

那么我们得重点分析一下这里面的每种情况的贡献:

  1. 首先观察到 0 的情况,当 x>y 时,上述三点没有任何一点满足,所以我们只需要考虑 1y 的数的贡献,直接分别考虑三种情况的贡献加起来就行了。

  2. 先考虑第二种情况,即 1<xy 中与 y 互质的数的个数,这个为欧拉函数的定义,即为 cnt=eulr[y]1,因为这里不包含 1。所以贡献为:cnt×u。主要这里面 x>1,所以少了一个 u,为 x=1 的贡献。

  3. 考虑第三种情况,gcd(x,y)=x,说明 xy 的因子,总贡献为:X×y,其中 X 为所有符合题意的 x 的和,即为 y 的因子和。但要注意一下,这里 x1,所以因子和里面要少个 1×y

  4. 考虑情况 1,我们发现二三情况总共少了个 uy 关于 x=1 的贡献,而 1 情况恰好补上了,那么我们可以这样处理 23 情况全部考虑 x=1 的贡献,然后不考虑 情况 1,这样贡献也正确了。

这样一来由上述分析可知道:

我们记:eulr[x] 表示 x 的欧拉函数值,pw[x] 为关于 x 的所有因数和,包括自身。

那么:

x=11e6f(x,ai)=u×eulr[ai]ai×pw[ai]

而这里面的 eulrpw 都可以用线性筛线性预处理:O(n),当然了,如果不会 pw 的线性预处理,也可以使用调和级数:O(nlnn) 预处理,即枚举每个数的所有倍数加入这个数的贡献。

我们现在回到原式:

发现是 l 是固定的,r 是需要枚举的,枚举出哪个是最优的 r,而无论如何,我们都涉及到了一个 [l,r] 上的答案查询,那么我们显然不可能暴力进行 [l,r] 上的答案查询,我们用传统的套路,考虑这个问题 可不可以差分ans(l,r)=ans(r)ans(l1),这题里是显而易见可以的,因为每一项都是独立的,并不涉及到若干项之间还有什么 maxgcd 之类的:

即设 val[i]=u×eulr[ai]ai×pw[ai],其中规定 val[0]=0

ans(l,r)=val[l]+val[l+1]+val[l+2]....val[r]

每一项 val[i] 都是独立的,显然可以有:

ans(r)=val[0]+val[1]+val[2]+val[3]+....val[r]

ans(l,r)=ans(r)ans(l1),,

我们容易知道 ans(l1) 为定值,因为 l 是题目给定的,而 r 是我们需要寻找的。并且 val[i] 经过预处理前缀和以后,我们是可以直接 O(1) 查询的。

ans(l1)=pre[l1]=val[0]+val[1]+val[2]....+val[l]

现在我们来到了寻找 r 的阶段,显然不可能枚举每个 r 然后去求最佳,肯定要用什么 log 之类的查询方式。

我们观察

ans(r)=pre[r]=i=0r(u×eulr[ai]ai×pw[ai])

=u×i=0reulr[ai]i=0rai×pw[ai]

k=i=0reulr[ai],b=i=0rai×pw[ai]

那么显然每个 r 都是一个 ans(r)=kr×u+br,一个一次函数。

那么其实思路就很简单了,一堆 带限制的 一次函数最大值查询。

关于斜率最值查询有很多工具:半平面交、凸壳二分/三分、李超线段树...

本题就讲讲个人比较喜欢用的两种,凸壳和李超树怎么分析和书写吧,半平面交只会一些板子题,等后续更深了再补。

凸包做法

关于凸壳求最大值:

  1. 构建下凸壳。

  2. 凸壳上二分。

先考虑第一个问题,第一个问题我们需要将 (k,b) 进行排序,然后再做单调队列。我们观察询问,rili,对于每个询问来说,它们的 (k,b) 集合是不相同的,不可能每次都重建凸壳,那么这时候我们需要考虑一下如何让凸壳不重建:

观察:

k=i=0reulr[ai],b=i=0rai×pw[ai]

这两个东西,当 r 变大,eulr[ai]>0,显然 k 单调递增。同理 ai×pw[ai]>0,所以 b 是单调递减的,当然我们的凸壳排序主要是跟 k 有关,那么我们观察到有:

k  r 

这意味着我们如果从左往右遍历,只需要在凸壳最后加入新的 (k,b),从右往左只需要在凸壳前面增加 (k,b),加入时同时跑单调队列均摊是 O(n) 的。

现在我们考虑所有的查询 l 有序,我们注意到 [l,n] 范围的集合是我们需要的,最好的就是每次比上一次多增加新的 (k,b),从左往右的话,我们的 l 增大,集合是变小的,不符合要求,当然可以用可撤销凸包实现,但没啥必要,所以我们考虑 l 从右往左,这样 l 变小,可选集合增大,每次只需要增加新进的 (k,b) 在凸包前部,然后跑单调队列维护下凸壳。查询就简单了,基本的凸包上二分或者三分最值即可。

最后我们来分析下数据范围:

首先 pre[r] 是前缀和,最坏的话假如取个 ai 为范围内的最大质数,那么前缀和显然是 1e12 级别。注意到 u2e7 左右,虽然还有减去后面前缀和的操作,但还是可能会爆 longlong,建议使用 int128,尤其是叉积。最后注意还有个相同最大值取最小 r,这个时候只需要在二分时候注意严格比较即可。

PS: 本题时限很大,所以本人一般只有最优解时才考虑用快读卡常,其余时刻尽量以比较简略的读入,方便读者阅读,读者可以在需要卡常的时候自行使用快读。

凸包二分+因数和调和级数预处理
#include <bits/stdc++.h>
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
// #define isPbdsFile
#ifdef isPbdsFile
#include <bits/extc++.h>
#else
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>
#endif
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
template <typename T>
int disc(T* a, int n)
{
return unique(a + 1, a + n + 1) - (a + 1);
}
template <typename T>
T lowBit(T x)
{
return x & -x;
}
template <typename T>
T Rand(T l, T r)
{
static mt19937 Rand(time(nullptr));
uniform_int_distribution<T> dis(l, r);
return dis(Rand);
}
template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
return (a % b + b) % b;
}
template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
a %= c;
T1 ans = 1;
for (; b; b >>= 1, (a *= a) %= c) if (b & 1) (ans *= a) %= c;
return modt(ans, c);
}
template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-') sign = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
x *= sign;
}
template <typename T, typename... U>
void read(T& x, U&... y)
{
read(x);
read(y...);
}
template <typename T>
void write(T x)
{
if (typeid(x) == typeid(char)) return;
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 ^ 48);
}
template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
write(x), putchar(c);
write(c, y...);
}
template <typename T11, typename T22, typename T33>
struct T3
{
T11 one;
T22 tow;
T33 three;
bool operator<(const T3 other) const
{
if (one == other.one)
{
if (tow == other.tow) return three < other.three;
return tow < other.tow;
}
return one < other.one;
}
T3()
{
one = tow = three = 0;
}
T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
{
}
};
template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
if (x < y) x = y;
}
template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
if (x > y) x = y;
}
typedef __int128 i128;
constexpr int N = 1e6 + 10;
constexpr i128 INF = 1e-20;
bool vis[N + 1];
vector<int> pri;
int eulr[N + 1];
ll pw[N + 1];
inline void init()
{
pw[1] = eulr[1] = 1;
forn(i, 2, N)
{
++pw[i]; //注意每一个都有1这个因子
for (int j = i; j <= N; j += i) pw[j] += i; //调和级数预处理
if (!vis[i]) pri.push_back(i), eulr[i] = i - 1;
for (const ll j : pri)
{
if (i * j > N) break;
vis[i * j] = true;
if (i % j == 0)
{
eulr[i * j] = eulr[i] * j;
break;
}
eulr[i * j] = eulr[i] * eulr[j];
}
}
}
struct Point
{
i128 k, b;
int idR;
i128 getVal(const i128 x) const
{
return k * x + b;
}
Point operator-(const Point& other) const
{
return Point(k - other.k, b - other.b);
}
};
inline i128 operator*(const Point& x, const Point& y)
{
return x.k * y.b - x.b * y.k;
}
deque<Point> hull;
//头部加入
inline void add(const Point& curr)
{
while (hull.size() > 1 and (hull[1] - hull[0]) * (hull[0] - curr) <= 0) hull.pop_front();
hull.push_front(curr);
}
//凸包上二分,越靠左的下标越小,所以我们注意严格小于才往右找
inline pair<i128, int> query(const ll x)
{
int l = 0, r = hull.size() - 1;
while (l < r)
{
const int mid = l + r + 1 >> 1;
if (mid and hull[mid - 1].getVal(x) < hull[mid].getVal(x)) l = mid;
else r = mid - 1;
}
return make_pair(hull[l].getVal(x), hull[l].idR);
}
int n, q;
ll a[N];
i128 k[N], b[N];
vector<tii> qu;
pair<i128, int> ans[N];
inline void solve()
{
cin >> n >> q;
forn(i, 1, n) cin >> a[i];
init();
//预处理(k,b)
forn(i, 1, n) k[i] = k[i - 1] + eulr[a[i]], b[i] = b[i - 1] - a[i] * pw[a[i]];
forn(i, 1, q)
{
int u, l;
cin >> u >> l;
qu.emplace_back(l, u, i);
}
//对l降序排列
sort(all(qu), greater());
int idx = n;
for (const auto [l,u,id] : qu)
{
//没加入的(k,b)给加入
while (idx >= l) add(Point(k[idx], b[idx], idx)), idx--;
ans[id] = query(u);
ans[id].first -= u * k[l - 1] + b[l - 1];
}
forn(i, 1, q) write(' ', ans[i].first, ans[i].second), putchar(endl);
}
signed int main()
{
// MyFile
Spider
//------------------------------------------------------
// clock_t start = clock();
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test) solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
// clock_t end = clock();
// cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

: O(VlnV),V=amax

: O(qlogn)

凸包+因数和线性筛预处理
#include <bits/stdc++.h>
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
// #define isPbdsFile
#ifdef isPbdsFile
#include <bits/extc++.h>
#else
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>
#endif
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
template <typename T>
int disc(T* a, int n)
{
return unique(a + 1, a + n + 1) - (a + 1);
}
template <typename T>
T lowBit(T x)
{
return x & -x;
}
template <typename T>
T Rand(T l, T r)
{
static mt19937 Rand(time(nullptr));
uniform_int_distribution<T> dis(l, r);
return dis(Rand);
}
template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
return (a % b + b) % b;
}
template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
a %= c;
T1 ans = 1;
for (; b; b >>= 1, (a *= a) %= c) if (b & 1) (ans *= a) %= c;
return modt(ans, c);
}
template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-') sign = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
x *= sign;
}
template <typename T, typename... U>
void read(T& x, U&... y)
{
read(x);
read(y...);
}
template <typename T>
void write(T x)
{
if (typeid(x) == typeid(char)) return;
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 ^ 48);
}
template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
write(x), putchar(c);
write(c, y...);
}
template <typename T11, typename T22, typename T33>
struct T3
{
T11 one;
T22 tow;
T33 three;
bool operator<(const T3 other) const
{
if (one == other.one)
{
if (tow == other.tow) return three < other.three;
return tow < other.tow;
}
return one < other.one;
}
T3()
{
one = tow = three = 0;
}
T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
{
}
};
template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
if (x < y) x = y;
}
template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
if (x > y) x = y;
}
typedef __int128 i128;
constexpr int N = 1e6 + 10;
bool vis[N + 1];
vector<int> pri;
int eulr[N + 1];
ll pw[N + 1], num[N + 1];
//线性预处理欧拉函数和因数和
inline void init()
{
pw[1] = eulr[1] = 1;
forn(i, 2, N)
{
if (!vis[i]) pri.push_back(i), eulr[i] = i - 1, pw[i] = num[i] = i + 1;
for (const ll j : pri)
{
if (i * j > N) break;
vis[i * j] = true;
if (i % j == 0)
{
eulr[i * j] = eulr[i] * j;
num[i * j] = num[i] * j + 1;
pw[i * j] = pw[i] / num[i] * (num[i] * j + 1);
break;
}
num[i * j] = j + 1;
pw[i * j] = pw[i] * pw[j];
eulr[i * j] = eulr[i] * eulr[j];
}
}
}
struct Point
{
i128 k, b;
int idR;
i128 getVal(const i128 x) const
{
return k * x + b;
}
Point operator-(const Point& other) const
{
return Point(k - other.k, b - other.b);
}
};
inline i128 operator*(const Point& x, const Point& y)
{
return x.k * y.b - x.b * y.k;
}
deque<Point> hull;
//头部加入(k,b)
inline void add(const Point& curr)
{
while (hull.size() > 1 and (hull[1] - hull[0]) * (hull[0] - curr) <= 0) hull.pop_front();
hull.push_front(curr);
}
//凸包上二分,注意一下头部的下标偏小,所以非严格大于就别往右找了,找最小的r
inline pair<i128, int> query(const ll x)
{
int l = 0, r = hull.size() - 1;
while (l < r)
{
const int mid = l + r + 1 >> 1;
if (hull[mid - 1].getVal(x) < hull[mid].getVal(x)) l = mid;
else r = mid - 1;
}
return make_pair(hull[l].getVal(x), hull[l].idR);
}
int n, q;
ll a[N];
i128 k[N], b[N];
vector<tii> qu;
pair<i128, int> ans[N];
inline void solve()
{
cin >> n >> q;
forn(i, 1, n) cin >> a[i];
init();
//预处理出(k,b)
forn(i, 1, n) k[i] = k[i - 1] + eulr[a[i]], b[i] = b[i - 1] - a[i] * pw[a[i]];
forn(i, 1, q)
{
int u, l;
cin >> u >> l;
qu.emplace_back(l, u, i);
}
//l降序排列
sort(all(qu), greater());
int idx = n;
for (const auto [l,u,id] : qu)
{
//加入新的(k,b)
while (idx >= l) add(Point(k[idx], b[idx], idx)), idx--;
ans[id] = query(u);
ans[id].first -= u * k[l - 1] + b[l - 1];
}
forn(i, 1, q) write(' ', ans[i].first, ans[i].second), putchar(endl);
}
signed int main()
{
// MyFile
Spider
//------------------------------------------------------
// clock_t start = clock();
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test) solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
// clock_t end = clock();
// cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

: O(V),V=amax

: O(qlogn)

李超树做法

关于李超树求最值:

  1. 如果维护的非值域线段,那么就是单 logMX,否则为 log2MX

  2. 由于是标记永久化,所以并不支持删除,但可以用可撤销或者可持久化进行 变向删除,同时支持动态开点。

  3. 李超树越上层的直线越优,本题由于比较除了值以外,还要比较同值情况下标小的更优,所以需要重载下比较函数、比较符。

我们从左往右和从右往左都讲讲。

从左往右,我们发现是相当于李超树中逐渐删除部分直线 (k,b),那么我们可以倒序加入直线 (k,b) 以后,按照 l 升序从左往右不断撤销操作,从而实现删除操作。由于每个加入操作都是 logMX,所以撤销同理。值域挺大的 2e7,可以考虑动态开点,但这题给的 1G,我们又只保存线段的 id,所以直接上静态李超树了。

升序 l + 可撤销李超树
#include <bits/stdc++.h>
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
// #define isPbdsFile
#ifdef isPbdsFile
#include <bits/extc++.h>
#else
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>
#endif
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
template <typename T>
int disc(T* a, int n)
{
return unique(a + 1, a + n + 1) - (a + 1);
}
template <typename T>
T lowBit(T x)
{
return x & -x;
}
template <typename T>
T Rand(T l, T r)
{
static mt19937 Rand(time(nullptr));
uniform_int_distribution<T> dis(l, r);
return dis(Rand);
}
template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
return (a % b + b) % b;
}
template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
a %= c;
T1 ans = 1;
for (; b; b >>= 1, (a *= a) %= c) if (b & 1) (ans *= a) %= c;
return modt(ans, c);
}
template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-') sign = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
x *= sign;
}
template <typename T, typename... U>
void read(T& x, U&... y)
{
read(x);
read(y...);
}
template <typename T>
void write(T x)
{
if (typeid(x) == typeid(char)) return;
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 ^ 48);
}
template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
write(x), putchar(c);
write(c, y...);
}
template <typename T11, typename T22, typename T33>
struct T3
{
T11 one;
T22 tow;
T33 three;
bool operator<(const T3 other) const
{
if (one == other.one)
{
if (tow == other.tow) return three < other.three;
return tow < other.tow;
}
return one < other.one;
}
T3()
{
one = tow = three = 0;
}
T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
{
}
};
template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
if (x < y) x = y;
}
template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
if (x > y) x = y;
}
typedef __int128 i128;
constexpr int N = 1e6 + 10;
constexpr int MX = 2e7;
constexpr i128 INF = -1e25;
bool vis[N + 1];
vector<int> pri;
int eulr[N + 1];
ll pw[N + 1], num[N + 1];
int n, q;
inline void init()
{
pw[1] = eulr[1] = 1;
forn(i, 2, N)
{
if (!vis[i]) pri.push_back(i), eulr[i] = i - 1, pw[i] = num[i] = i + 1;
for (const ll j : pri)
{
if (i * j > N) break;
vis[i * j] = true;
if (i % j == 0)
{
eulr[i * j] = eulr[i] * j;
num[i * j] = num[i] * j + 1;
pw[i * j] = pw[i] / num[i] * (num[i] * j + 1);
break;
}
num[i * j] = j + 1;
pw[i * j] = pw[i] * pw[j];
eulr[i * j] = eulr[i] * eulr[j];
}
}
}
stack<pii> back;
int cnt[N];
struct Seg
{
i128 k, b;
i128 getVal(const i128 x) const
{
return k * x + b;
}
} seg[N];
int segId[MX << 2];
//重写比较,值相同比下标,线段ID即为下标
inline bool check(const int idx, const int idy, const int x)
{
const i128 a = seg[idx].getVal(x);
const i128 b = seg[idy].getVal(x);
if (a == b) return idx > idy;
return a < b;
}
//修改时将操作保留在撤销栈中
inline void add(const int curr, int val, const int l = 1, const int r = MX)
{
if (!segId[curr])
{
back.emplace(curr, segId[curr]);
segId[curr] = val;
return;
}
const int mid = l + r >> 1;
if (check(segId[curr], val, mid))
{
back.emplace(curr, segId[curr]);
swap(segId[curr], val);
}
if (check(segId[curr], val, l)) add(ls(curr), val, l, mid);
if (check(segId[curr], val, r)) add(rs(curr), val, mid + 1, r);
}
typedef pair<i128, int> pAns;
//重写比较,值相同比下标谁更小,线段ID即为下标
inline bool operator<(const pAns& x, const pAns& y)
{
if (x.first != y.first) return x.first < y.first;
return x.second > y.second;
}
inline void merge(pAns& curr, const pAns& other)
{
if (curr < other) curr = other;
}
inline pAns query(const int curr, const int val, const int l = 1, const int r = MX)
{
if (!segId[curr]) return pAns(INF, n + 1);
auto ans = pair(seg[segId[curr]].getVal(val), segId[curr]);
if (l == r) return ans;
const int mid = l + r >> 1;
if (val <= mid) merge(ans, query(ls(curr), val, l, mid));
else merge(ans, query(rs(curr), val, mid + 1, r));
return ans;
}
ll a[N];
i128 k[N], b[N];
vector<tii> qu;
pAns ans[N];
inline void solve()
{
cin >> n >> q;
forn(i, 1, n) cin >> a[i];
init();
forn(i, 1, n) k[i] = k[i - 1] + eulr[a[i]], b[i] = b[i - 1] - a[i] * pw[a[i]];
//倒序加入,并记录每次栈中剩余操作数量
forv(i, n, 1)
{
seg[i] = Seg{k[i], b[i]};
add(1, i);
cnt[i] = back.size();
}
forn(i, 1, q)
{
int u, l;
cin >> u >> l;
qu.emplace_back(l, u, i);
}
//升序排列
sort(all(qu));
for (const auto [l,u,id] : qu)
{
//可撤销李超树撤销操作即为删除操作
while (back.size() > cnt[l])
{
const auto [curr,segVal] = back.top();
segId[curr] = segVal;
back.pop();
}
auto [val, idx] = query(1, u);
val -= u * k[l - 1] + b[l - 1];
ans[id] = pAns(val, idx);
}
forn(i, 1, q) write(' ', ans[i].first, ans[i].second), putchar(endl);
}
signed int main()
{
// MyFile
Spider
//------------------------------------------------------
// clock_t start = clock();
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test) solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
// clock_t end = clock();
// cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

: O(V),V=amax

: O((n+q)logn)

倒序,就是普通的李超树了,直接不断地加入线段和做值域查询。

降序 l + 普通李超树查询
#include <bits/stdc++.h>
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
// #define isPbdsFile
#ifdef isPbdsFile
#include <bits/extc++.h>
#else
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>
#endif
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
template <typename T>
int disc(T* a, int n)
{
return unique(a + 1, a + n + 1) - (a + 1);
}
template <typename T>
T lowBit(T x)
{
return x & -x;
}
template <typename T>
T Rand(T l, T r)
{
static mt19937 Rand(time(nullptr));
uniform_int_distribution<T> dis(l, r);
return dis(Rand);
}
template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
return (a % b + b) % b;
}
template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
a %= c;
T1 ans = 1;
for (; b; b >>= 1, (a *= a) %= c) if (b & 1) (ans *= a) %= c;
return modt(ans, c);
}
template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-') sign = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
x *= sign;
}
template <typename T, typename... U>
void read(T& x, U&... y)
{
read(x);
read(y...);
}
template <typename T>
void write(T x)
{
if (typeid(x) == typeid(char)) return;
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 ^ 48);
}
template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
write(x), putchar(c);
write(c, y...);
}
template <typename T11, typename T22, typename T33>
struct T3
{
T11 one;
T22 tow;
T33 three;
bool operator<(const T3 other) const
{
if (one == other.one)
{
if (tow == other.tow) return three < other.three;
return tow < other.tow;
}
return one < other.one;
}
T3()
{
one = tow = three = 0;
}
T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
{
}
};
template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
if (x < y) x = y;
}
template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
if (x > y) x = y;
}
typedef __int128 i128;
constexpr int N = 1e6 + 10;
constexpr int MX = 2e7;
constexpr i128 INF = -1e25;
bool vis[N + 1];
vector<int> pri;
int eulr[N + 1];
ll pw[N + 1], num[N + 1];
inline void init()
{
pw[1] = eulr[1] = 1;
forn(i, 2, N)
{
if (!vis[i]) pri.push_back(i), eulr[i] = i - 1, pw[i] = num[i] = i + 1;
for (const ll j : pri)
{
if (i * j > N) break;
vis[i * j] = true;
if (i % j == 0)
{
eulr[i * j] = eulr[i] * j;
num[i * j] = num[i] * j + 1;
pw[i * j] = pw[i] / num[i] * (num[i] * j + 1);
break;
}
num[i * j] = j + 1;
pw[i * j] = pw[i] * pw[j];
eulr[i * j] = eulr[i] * eulr[j];
}
}
}
struct Seg
{
i128 k, b;
i128 getVal(const i128 x) const
{
return k * x + b;
}
} seg[N];
int segId[MX << 2];
inline bool check(const int idx, const int idy, const int x)
{
const i128 a = seg[idx].getVal(x);
const i128 b = seg[idy].getVal(x);
if (a == b) return idx > idy;
return a < b;
}
inline void add(const int curr, int val, const int l = 1, const int r = MX)
{
if (!segId[curr])
{
segId[curr] = val;
return;
}
const int mid = l + r >> 1;
if (check(segId[curr], val, mid)) swap(segId[curr], val);
if (check(segId[curr], val, l)) add(ls(curr), val, l, mid);
if (check(segId[curr], val, r)) add(rs(curr), val, mid + 1, r);
}
typedef pair<i128, int> pAns;
inline bool operator<(const pAns& x, const pAns& y)
{
if (x.first != y.first) return x.first < y.first;
return x.second > y.second;
}
inline void merge(pAns& curr, const pAns& other)
{
if (curr < other) curr = other;
}
inline pAns query(const int curr, const int val, const int l = 1, const int r = MX)
{
if (!segId[curr]) return pAns(INF, INF);
auto ans = pair(seg[segId[curr]].getVal(val), segId[curr]);
if (l == r) return ans;
const int mid = l + r >> 1;
if (val <= mid) merge(ans, query(ls(curr), val, l, mid));
else merge(ans, query(rs(curr), val, mid + 1, r));
return ans;
}
int n, q;
ll a[N];
i128 k[N], b[N];
vector<tii> qu;
pair<i128, int> ans[N];
inline void solve()
{
cin >> n >> q;
forn(i, 1, n) cin >> a[i];
init();
forn(i, 1, n) k[i] = k[i - 1] + eulr[a[i]], b[i] = b[i - 1] - a[i] * pw[a[i]];
forn(i, 1, n) seg[i] = Seg{k[i], b[i]};
forn(i, 1, q)
{
int u, l;
cin >> u >> l;
qu.emplace_back(l, u, i);
}
sort(all(qu), greater());
//倒序遍历l
int idx = n + 1;
for (const auto [l,u,id] : qu)
{
while (idx > l) add(1, --idx);
ans[id] = query(1, u);
ans[id].first -= u * k[l - 1] + b[l - 1];
}
forn(i, 1, q) write(' ', ans[i].first, ans[i].second), putchar(endl);
}
signed int main()
{
// MyFile
Spider
//------------------------------------------------------
// clock_t start = clock();
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test) solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
// clock_t end = clock();
// cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

: O(V),V=amax

: O((n+q)logn)

posted @   Athanasy  阅读(211)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示