首页 > 其他分享 >优美函数 题解

优美函数 题解

时间:2024-10-01 21:45:27浏览次数:8  
标签:优美 函数 int 题解 mid varphi cdot dfrac sigma

题意简述

给你函数 \(f\):

\[f(x,y,u)=\left\{ \begin{array}{rcl} u - y, & x=1 \\ u, & 1 < x \le y, \ \gcd(x, y)=1 \\ -x \cdot y, & x\neq 1, \ \gcd(x, y)=x \\ 0, & \text{otherwise}. \end{array} \right. \]

对于一个长度为 \(n\) 的序列 \(a\),定义函数 \(g\):

\[g(l, r, u) = \sum_{i=1}^{10^6} \sum_{j=l}^r f(i,a_j,u) \]

给你 \(q\) 个询问,告诉你 \((l, u)\),求 \(\max \limits _ {r = l} ^ n g(l, r, u)\),以及对应的 \(r\),多个最值输出 \(r\) 较小的。

\(n, q \leq 5 \times 10^5\),\(1 \leq a_i \leq 10^6\),\(1 \leq u \leq 1.8 \times 10^7\)。

题目分析

显然要先观察 \(f\),我们把 \(\sum \limits _ {i = 1} ^ {10^6} f(i, a_j, u)\) 的代码写出来:

lint res = 0;
for (int i = 1; i <= 1000000; ++i) {
    if (i == 1) res += u - a[j];
    else if (i <= a[j] && gcd(i, a[j]) == 1) res += u;
    else if (gcd(i, a[j]) == i) res += -i * a[j];
}

显然要把 \(i = 1\) 提出来,然后后面两个 if 是不交的,可以分别考虑。发现此时 \(i\) 的上界可以下调至 \(a_j\)。\(\gcd(i, a_j) = i\) 等价于 \(i \mid a_j\)。再把不变的东西提出来,可以得到如下代码:

lint res = u - a[j];
lint cnt = 0, sum = 0;
for (int i = 2; i <= a[j]; ++i) {
    if (gcd(i, a[j]) == 1) ++cnt;
    if (a[j] % i == 0) sum += i;
}
res += cnt * u, res -= sum;

我们惊奇的发现,\(u - a_j\) 满足我们 for 里面两个 if 的条件,可以放回去:

lint cnt = 0, sum = 0;
for (int i = 1; i <= a[j]; ++i) {
    if (gcd(i, a[j]) == 1) ++cnt;
    if (a[j] % i == 0) sum += i;
}
lint res = cnt * u - sum;

这时候,\(cnt\) 和 \(sum\) 就有意义了,分别表示:「\(1 \sim a_j\) 中和 \(a_j\) 互质的数的个数」、「\(a_j\) 的所有约数之和」,也就是 \(\varphi(a_j)\) 和 \(\sigma(a_j)\),这两个都可以线性筛地搞。

如果你不会

考场上忘了,只好现推一遍。其实以前根本不知道啥是 \(\sigma\),但是实力地推出式子来了!我们只用考虑 \(i \bmod p_j \neq 0\) 和 \(i \bmod p_j = 0\) 两种情况。

  1. \(\varphi(x)\)。

    1. \(i \bmod p_j \neq 0\)。
      经典积性函数曾说:如果你给我 \(a \perp b\),我能告诉你,\(\varphi(ab) = \varphi(a)\varphi(b)\)。所以 \(\varphi(i \cdot p_j) = \varphi(i) \varphi(p_j)\)。
    2. \(i \bmod p_j = 0\)。
      我们记 \(i = p_j ^ t \prod \limits _ {p_k \mid i} p_k ^ {t_k}\)。考虑欧拉函数计算式 \(\varphi(x) = x \prod \limits _ {p_j \mid x} \dfrac{p_j - 1}{p_j}\),就有 \(\varphi(i) = \varphi\left(\dfrac{i}{p_j ^ t} \right) \dfrac{p_j - 1}{p_j}\)。而 \(\dfrac{i}{p_j ^ t} \perp p_j ^ t\),所以 \(\varphi(i \cdot p_j) = \varphi\left(\dfrac{i}{p_j ^ t} p_j^{t+1} \right) = \varphi\left(\dfrac{i}{p_j ^ t} \right) \varphi(p_j^{t+1}) = \varphi(i) \dfrac{p_j}{p_j - 1} (p_j - 1) = \varphi(i) p_j\)。
  2. \(\sigma(x)\)。
    考虑 \(x = \prod \limits _ {p_k \mid x} p_k ^ {t_k}\),那么 \(\sigma(x) = \operatorname{sum}\left\{ \prod p_k ^ {t'_k} \mid t'_k \leq t_k \right\}\),由于 \(t'_k\) 之间互不影响,考虑乘法原理,\(\sigma(x) = \prod (1 + p_k + \ldots + p_k ^ {t_k})\),这样一定能凑出所有 \(x\) 的因数。

    1. \(i \bmod p_j \neq 0\)。
      \(\sigma(i \cdot p_j) = \sigma(i) (1 + p_j) = \sigma(i) \sigma(p_j)\)。这里没有一步到位的原因是,我之前不知道 \(\sigma\),也就没想它是不是积性函数了。
    2. \(i \bmod p_j = 0\)。
      同样设 \(i\) 中有 \(p_j ^ t\),那么 \(\sigma(i \cdot p_j) = \sigma\left(\dfrac{i}{p_j^t}\right) (1 + p_j + \cdots + p_j ^ {t + 1})\),这很难受,考虑变换一下 \(\sigma\left(\dfrac{i}{p_j^t}\right) (1 + p_j + \cdots + p_j ^ {t + 1}) = \sigma(i) + \sigma\left(\dfrac{i}{p_j^t}\right) p_j ^ {t + 1}\)。接下来,我们只需要对一个 \(x\) 维护 \(c(x)\) 表示 \(p^t\),其中 \(p\) 是 \(x\) 的最小质因数,\(t\) 是幂次。这个很好维护,当 \(i \bmod p_j \neq 0\),\(c(i \cdot p_j) = p_j\);否则,\(c(i \cdot p_j) = c(i) p_j\)。如此,\(\sigma(i \cdot p_j) = \sigma(i) + \sigma\left(\dfrac{i}{c(i)}\right) c(i) p_j\)

对于一次询问,求的式子可以简化成:

\[\max _ {r = l} ^ n \left\{ \sum _ {i = l} ^ r \varphi(a_i) \cdot u + \sigma(a_i) \right\} \]

对于一个 \(i\) 来说,\(\varphi(a_i)\) 和 \(\sigma(a_i)\) 都是定值,我们可以把它们分别做个前缀和。由于很像一次函数,我们将这两个前缀和记作 \(K, B\)。

\[\begin{aligned} &\quad \ \max _ {r = l} ^ n \Big\{ K_r - K_{l - 1} \cdot u + B_r - B_{l - 1} \Big\} \\ & = \max _ {r = l} ^ n \Big\{ K_r \cdot u + B_r \Big\} - K_{l - 1} \cdot u - B_{l - 1} \\ \end{aligned} \]

一次函数最值,且 \(K\) 有单调性,我们考虑凸包上二分。我们考虑把查询离线到 \(l\) 上,从右往左扫,维护一个下凸包(一次函数的最值),查询最值就去二分出 \(x\) 在凸包那条直线上。

时间复杂度:\(\Theta(V + q \log n)\)。

代码

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

const int N = 500010, V = 1000010;

using lint = long long;

int n, q, val[N];
int phi[V], prime[V], pcnt, cj[V];
bool nprime[V];
lint subsum[V], K[N], B[N];
vector<pair<int, int>> qry[N];
lint mx[N];
int mxR[N];

int stack[N], top;

inline bool cmptail(int a, int b, int c) {
    return (__int128_t)(B[c] - B[a]) * (K[a] - K[b]) > (__int128_t)(B[b] - B[a]) * (K[a] - K[c]);
}

inline void insert(int idx) {
    while (top - 1 >= 1 && cmptail(stack[top - 1], stack[top], idx)) --top;
    stack[++top] = idx;
}

inline int query(int x) {
    int l = 1, r = top - 1, res = stack[top];
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (1ll * x * (K[stack[mid]] - K[stack[mid + 1]]) >= B[stack[mid + 1]] - B[stack[mid]])
            res = stack[mid], r = mid - 1;
        else
            l = mid + 1;
    }
    return res;
}

signed main() {
    #ifndef XuYueming
    freopen("function.in", "r", stdin);
    freopen("function.out", "w", stdout);
    #endif
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &val[i]);
    phi[1] = 1, subsum[1] = 1;
    for (int i = 2; i <= 1000000; ++i) {
        if (!nprime[i]) prime[++pcnt] = i, phi[i] = i - 1, subsum[i] = 1 + i, cj[i] = i;
        for (int j = 1; j <= pcnt && i * prime[j] <= 1000000; ++j) {
            nprime[i * prime[j]] = true;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                subsum[i * prime[j]] = subsum[i] + subsum[i / cj[i]] * cj[i] * prime[j];
                cj[i * prime[j]] = cj[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * phi[prime[j]];
            subsum[i * prime[j]] = subsum[i] * subsum[prime[j]];
            cj[i * prime[j]] = prime[j];
        }
    }
    for (int i = 1; i <= n; ++i) {
        K[i] = phi[val[i]];
        B[i] = -1ll * subsum[val[i]] * val[i];
        K[i] += K[i - 1];
        B[i] += B[i - 1];
    }
    for (int i = 1, u, l; i <= q; ++i) {
        scanf("%d%d", &u, &l);
        qry[l].emplace_back(u, i);
    }
    for (int i = n; i >= 1; --i) {
        insert(i);
        for (auto [x, idx]: qry[i]) {
            int ps = query(x);
            mx[idx] = (1ll * x * K[ps] + B[ps]) - (1ll * x * K[i - 1] + B[i - 1]);
            mxR[idx] = ps;
        }
    }
    for (int i = 1; i <= q; ++i)
        printf("%lld %d\n", mx[i], mxR[i]);
    return 0;
}

标签:优美,函数,int,题解,mid,varphi,cdot,dfrac,sigma
From: https://www.cnblogs.com/XuYueming/p/18443164

相关文章

  • ABC 模拟赛 | A Passing Contest 001题解记录(A,B,C,D)
    比赛链接https://www.luogu.com.cn/contest/126344[APC001]A-CT不必多说,多次取模#include<iostream>#include<algorithm>#include<string>#include<string.h>#include<queue>#include<deque>#include<math.h>#include<map>......
  • CF1214G Feeling Good 题解
    题目链接点击打开链接题目解法我真菜啊,感觉每一步都不难,但一步都没想到/yun考虑两行\(x,y\)什么时候可以构造出合法的矩形?即\(x\)中需要有\(y\)对应位置为\(0\)的\(1\),\(y\)中需要有\(x\)对应位置为\(0\)的\(1\)归纳一下,\(x\)不是\(y\)的子集且\(y\)不......
  • CF2018E2 Complex Segments (Hard Version) 题解
    题目描述\(T\)组数据,给定\(n\)条线段\([l_i,r_i]\),称一个线段集合是复杂的,当且仅当:它可以被划分成若干个大小相等的线段组。两条线段相交当且仅当它们在同一组。求用这\(n\)条线段构成的复杂线段集合的最大值。数据范围\(1\len,\sumn\le3\cdot10^5\)。\(1\l......
  • CF280C Game on Tree题解
    题目描述给定一棵有根树,结点编号从1到n。根结点为1号结点。对于每一次操作,等概率的选择一个尚未被删去的结点并将它及其子树全部删去。当所有结点被删除之后游戏结束。也就是说,删除1号结点后游戏即结束。要求求出删除所有结点的期望操作次数。不是哥们,我好不容易国庆......
  • 字符函数和字符串函数
    一.字符转换函数C语言提供2个字符转换函数tolower  toupper       他可以转换大小写 二.strlen的使用和模拟实现字符串以\0作为结束标志,strlen返回的是在strlen前面出现的字符个数(不包含\0)strlen函数模拟实现递归的方法 创建临时变量计数器 指针......
  • 【PostgreSQL】提高篇——如何创建和使用自定义函数和存储过程,包括 PL/pgSQL 语言的使
    数据库管理中,存储过程和自定义函数是非常重要的概念,尤其是在使用PostgreSQL这样的关系数据库管理系统时。它们允许开发者将复杂的业务逻辑封装在数据库中,从而提高应用程序的性能、可维护性和安全性。使用PL/pgSQL语言编写的存储过程和函数可以实现数据处理、事务控制和复......
  • 【编程小白必看】MySQL 聚合函数操作秘籍一文全掌握
    【编程小白必看】MySQL聚合函数操作秘籍......
  • 题解:AtCoder Beginner Contest AT_abc373_d ABC373D Hidden Weights(格式美化版)
    题目传送门题目翻译给你一个NNN个点,MMM条边的有向图,其中边有边......
  • CF429E Points and Segments 题解
    题目链接点击打开链接题目解法真难啊/yun把区间染成红色看作区间\(+1\),染成蓝色看作区间\(-1\),要求是每个点上的数\(\in\{-1,0,1\}\)可以选择的数有\(-1,1\)不太好做,我们考虑将限制变成每个点上的数只能为\(0\)我们记经过点\(x\)的线段数量为\(cnt_x\)如果\(cnt......
  • 题解:P11075 不等关系 加强版
    这是洛谷转移过来的题解,作者是4041nofoundGeoge(我自己,记得关注呀)题目大意对于一个字符串s1,s......