題目連結:CF 或者 洛谷
PS: CF的得等上gym。
前提說明
其實在上個月就見到這題了,當時很想做這題,結果找不到做題連結,也不知道出處,原來是陝西省賽的捧杯題。
個人評價覺得是一道很不錯的題,難度適中。
講解
其實題解寫的挺不錯的,比很多比賽的題解寫的詳細許多了。這裡站在我的角度分析這題吧:
先觀察要求的式子:
其中
\(u\) 是題目給定查詢的。看到這一坨就很麻煩,我們看看詢問啥東西:
那看來肯定是需要變形了,我們重點分析一下:
那麼我們得重點分析一下這裡面的每種情況的貢獻:
-
首先觀察到 \(0\) 的情況,當 \(x>y\) 時,上述三點沒有任何一點滿足,所以我們只需要考慮 \(1\sim y\) 的數的貢獻,直接分別考慮三種情況的貢獻加起來就行了。
-
先考慮第二種情況,即 \(1< x\le y\) 中與 \(y\) 互質的數的個數,這個為尤拉函式的定義,即為 \(cnt=eulr[y]-1\),因為這裡不包含 \(1\)。所以貢獻為:\(cnt \times u\)。主要這裡面 \(x>1\),所以少了一個 \(u\),為 \(x=1\) 的貢獻。
-
考慮第三種情況,\(\gcd(x,y)=x\),說明 \(x\) 是 \(y\) 的因子,總貢獻為:\(-X \times y\),其中 \(X\) 為所有符合題意的 \(x\) 的和,即為 \(y\) 的因子和。但要注意一下,這裡 \(x \neq 1\),所以因子和裡面要少個 \(-1 \times y\)。
-
考慮情況 \(1\),我們發現二三情況總共少了個 \(u-y\) 關於 \(x=1\) 的貢獻,而 \(1\) 情況恰好補上了,那麼我們可以這樣處理 \(2\) 和 \(3\) 情況全部考慮 \(x=1\) 的貢獻,然後不考慮 情況 \(1\),這樣貢獻也正確了。
這樣一來由上述分析可知道:
我們記:\(eulr[x]\) 表示 \(x\) 的尤拉函式值,\(pw[x]\) 為關於 \(x\) 的所有因數和,包括自身。
那麼:
而這裡面的 \(eulr\) 和 \(pw\) 都可以用線性篩線性預處理:\(O(n)\),當然了,如果不會 \(pw\) 的線性預處理,也可以使用調和級數:\(O(n\ln{n})\) 預處理,即列舉每個數的所有倍數加入這個數的貢獻。
我們現在回到原式:
發現是 \(l\) 是固定的,\(r\) 是需要列舉的,列舉出哪個是最優的 \(r\),而無論如何,我們都涉及到了一個 \([l,r]\) 上的答案查詢,那麼我們顯然不可能暴力進行 \([l,r]\) 上的答案查詢,我們用傳統的套路,考慮這個問題 可不可以差分:\(ans(l,r)=ans(r)-ans(l-1)\),這題裡是顯而易見可以的,因為每一項都是獨立的,並不涉及到若干項之間還有什麼 \(\max\),\(\gcd\) 之類的:
即設 \(val[i]=u \times eulr[a_i]-a_i \times pw[a_i]\),其中規定 \(val[0]=0\):
每一項 \(val[i]\) 都是獨立的,顯然可以有:
我們容易知道 \(ans(l-1)\) 為定值,因為 \(l\) 是題目給定的,而 \(r\) 是我們需要尋找的。並且 \(val[i]\) 經過預處理字首和以後,我們是可以直接 \(O(1)\) 查詢的。
即 \(ans(l-1)=pre[l-1]=val[0]+val[1]+val[2]....+val[l]\)。
現在我們來到了尋找 \(r\) 的階段,顯然不可能列舉每個 \(r\) 然後去求最佳,肯定要用什麼 \(\log\) 之類的查詢方式。
我們觀察
那麼顯然每個 \(r\) 都是一個 \(ans(r)=k_r \times u +b_r\),一個一次函式。
那麼其實思路就很簡單了,一堆 帶限制的 一次函式最大值查詢。
關於斜率最值查詢有很多工具:半平面交、凸殼二分/三分、李超線段樹...
本題就講講個人比較喜歡用的兩種,凸殼和李超樹怎麼分析和書寫吧,半平面交只會一些板子題,等後續更深了再補。
凸包做法
關於凸殼求最大值:
-
構建下凸殼。
-
凸殼上二分。
先考慮第一個問題,第一個問題我們需要將 \((k,b)\) 進行排序,然後再做單調佇列。我們觀察詢問,\(r_i \ge l_i\),對於每個詢問來說,它們的 \((k,b)\) 集合是不相同的,不可能每次都重建凸殼,那麼這時候我們需要考慮一下如何讓凸殼不重建:
觀察:
這兩個東西,當 \(r\) 變大,\(eulr[a_i]>0\),顯然 \(k\) 單調遞增。同理 \(a_i \times pw[a_i]>0\),所以 \(b\) 是單調遞減的,當然我們的凸殼排序主要是跟 \(k\) 有關,那麼我們觀察到有:
這意味著我們如果從左往右遍歷,只需要在凸殼最後加入新的 \((k,b)\),從右往左只需要在凸殼前面增加 \((k,b)\),加入時同時跑單調佇列均攤是 \(O(n)\) 的。
現在我們考慮所有的查詢 \(l\) 有序,我們注意到 \([l,n]\) 範圍的集合是我們需要的,最好的就是每次比上一次多增加新的 \((k,b)\),從左往右的話,我們的 \(l\) 增大,集合是變小的,不符合要求,當然可以用可撤銷凸包實現,但沒啥必要,所以我們考慮 \(l\) 從右往左,這樣 \(l\) 變小,可選集合增大,每次只需要增加新進的 \((k,b)\) 在凸包前部,然後跑單調佇列維護下凸殼。查詢就簡單了,基本的凸包上二分或者三分最值即可。
最後我們來分析下資料範圍:
首先 \(pre[r]\) 是字首和,最壞的話假如取個 \(a_i\) 為範圍內的最大質數,那麼字首和顯然是 \(1e12\) 級別。注意到 \(u\) 為 \(2e7\) 左右,雖然還有減去後面字首和的操作,但還是可能會爆 \(long long\),建議使用 \(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;
}
凸包+因數和線性篩預處理
#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;
}
李超樹做法
關於李超樹求最值:
-
如果維護的非值域線段,那麼就是單 \(\log{MX}\),否則為 \(\log^2{MX}\)。
-
由於是標記永久化,所以並不支援刪除,但可以用可撤銷或者可持久化進行 變向刪除,同時支援動態開點。
-
李超樹越上層的直線越優,本題由於比較除了值以外,還要比較同值情況下標小的更優,所以需要過載下比較函式、比較符。
我們從左往右和從右往左都講講。
從左往右,我們發現是相當於李超樹中逐漸刪除部分直線 \((k,b)\),那麼我們可以倒序加入直線 \((k,b)\) 以後,按照 \(l\) 升序從左往右不斷撤銷操作,從而實現刪除操作。由於每個加入操作都是 \(\log{MX}\),所以撤銷同理。值域挺大的 \(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;
}
倒序,就是普通的李超樹了,直接不斷地加入線段和做值域查詢。
降序 $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;
}