[SNCPC2024] 2024 年陕西省大学生程序设计 J题猜质数II 题解
题目链接:CF 或者 洛谷
前提说明
其实在上个月就见到这题了,当时很想做这题,结果找不到做题链接,也不知道出处,原来是陕西省赛的捧杯题。
个人评价觉得是一道很不错的题,难度适中。
讲解
其实题解写的挺不错的,比很多比赛的题解写的详细许多了。这里站在我的角度分析这题吧:
先观察要求的式子:
其中
那看来肯定是需要变形了,我们重点分析一下:
那么我们得重点分析一下这里面的每种情况的贡献:
-
首先观察到
的情况,当 时,上述三点没有任何一点满足,所以我们只需要考虑 的数的贡献,直接分别考虑三种情况的贡献加起来就行了。 -
先考虑第二种情况,即
中与 互质的数的个数,这个为欧拉函数的定义,即为 ,因为这里不包含 。所以贡献为: 。主要这里面 ,所以少了一个 ,为 的贡献。 -
考虑第三种情况,
,说明 是 的因子,总贡献为: ,其中 为所有符合题意的 的和,即为 的因子和。但要注意一下,这里 ,所以因子和里面要少个 。 -
考虑情况
,我们发现二三情况总共少了个 关于 的贡献,而 情况恰好补上了,那么我们可以这样处理 和 情况全部考虑 的贡献,然后不考虑 情况 ,这样贡献也正确了。
这样一来由上述分析可知道:
我们记:
那么:
而这里面的
我们现在回到原式:
发现是
即设
每一项
我们容易知道
即
现在我们来到了寻找
我们观察
那么显然每个
那么其实思路就很简单了,一堆 带限制的 一次函数最大值查询。
关于斜率最值查询有很多工具:半平面交、凸壳二分/三分、李超线段树...
本题就讲讲个人比较喜欢用的两种,凸壳和李超树怎么分析和书写吧,半平面交只会一些板子题,等后续更深了再补。
凸包做法
关于凸壳求最大值:
-
构建下凸壳。
-
凸壳上二分。
先考虑第一个问题,第一个问题我们需要将
观察:
这两个东西,当
这意味着我们如果从左往右遍历,只需要在凸壳最后加入新的
现在我们考虑所有的查询
最后我们来分析下数据范围:
首先
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; }
凸包+因数和线性筛预处理
#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; }
李超树做法
关于李超树求最值:
-
如果维护的非值域线段,那么就是单
,否则为 。 -
由于是标记永久化,所以并不支持删除,但可以用可撤销或者可持久化进行 变向删除,同时支持动态开点。
-
李超树越上层的直线越优,本题由于比较除了值以外,还要比较同值情况下标小的更优,所以需要重载下比较函数、比较符。
我们从左往右和从右往左都讲讲。
从左往右,我们发现是相当于李超树中逐渐删除部分直线
升序 + 可撤销李超树
#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; }
倒序,就是普通的李超树了,直接不断地加入线段和做值域查询。
降序 + 普通李超树查询
#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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)