算法刷题day19

目录

引言

这几道题主要都是考察数学,如果数学学的不好,推不出来公式,或者是你的数学思维不好都是做不出来的,所以说数学真的很重要,不过其实能考的数学也就那几个,无非就是很多而已,把常见的题型会了就行了,加油!


一、因数平方和

标签:数学、贡献法、整数划分

思路:如果是单一求每一个 f ( x ) f(x) f(x) 那么即使每一个是 O ( l o g N ) O(logN) O(logN)的时间复杂度,那么也要 O ( N l o g N ) O(NlogN) O(NlogN),而 n < = 1 0 9 n<=10^9 n<=109明显已经超时了,所以可以换一种思路。用贡献法,那么每个约数 i i i 对结果的贡献应该是 ∑ i = 1 n n i ⋅ i 2 \sum_{i=1}^{n} \frac{n}{i}\cdot i^2 ∑i=1nin⋅i2 ,而最多只可能有 2 n 2\sqrt{n} 2n 个不同的约数,而这个可以用整数划分来做,把每一个 n i \frac{n}{i} in相等的数划分到一块,这样就可以根据平方和公式来 i 2 i^2 i2了,然后可以用二分或者公式求出 n y = x \frac{n}{y}=x yn=x的最大的y,也就是 n x \frac{n}{x} xn ,最后处理一下 S n S_n Sn 即可,可用逆元或者__int128来处理。 平方和公式: S n = n ⋅ ( n + 1 ) ⋅ ( 2 n + 1 ) 6 平方和公式:S_n = \frac{n\cdot(n+1)\cdot(2n+1)}{6} 平方和公式:Sn=6n⋅(n+1)⋅(2n+1)

题目描述:

记 f(x) 为 x 的所有因数(约数)的平方的和。

例如:f(12)=12+22+32+42+62+122。

定义 g(n)=∑i=1nf(i)。

给定 n,求 g(n) 除以 109+7 的余数。

输入格式
输入一行包含一个正整数 n。

输出格式
输出一个整数表示答案 g(n) 除以 109+7 的余数。

数据范围
对于 20% 的评测用例,n≤105。
对于 30% 的评测用例,n≤107。
对于所有评测用例,1≤n≤109。

输入样例:
100000
输出样例:
680584257

示例代码一:__int128写法 (1e38)

cpp 复制代码
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int MOD = 1e9+7;

int n;

LL calc(int n)
{
    return n * (__int128)(n + 1) * (2 * n + 1) / 6 % MOD;
}

int main()
{
    cin >> n;
    
    LL res = 0;
    for(int i = 1; i <= n;)
    {
        int x = n / i, y = n / x;
        res = (res + (calc(y) - calc(i-1)) * x) % MOD;
        i = y + 1;
    }
    
    cout << (res + MOD) % MOD << endl;
    
    return 0;
}

示例代码二:逆元写法

cpp 复制代码
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int MOD = 1e9+7;

int n;

//这样求也可以,前提是6与MOD是互质的
// LL qmi(LL a, LL k, int p)
// {
//     LL res = 1;
//     while(k)
//     {
//         if(k & 1) res = res * a % p;
//         k >>= 1;
//         a = a * a % p;
//     }
    
//     return res;
// }

// LL calc(int n)
// {
//     return n * (n + 1ll) % MOD * (2 * n + 1) % MOD * qmi(6,MOD-2,MOD) % MOD;
// }

LL calc(int n)
{
    return n * (n + 1ll) % MOD * (2 * n + 1) % MOD * 166666668 % MOD;
}

int main()
{
	//逆元就是除上一个数取模和乘上一个数取模的结果一样
    // for(int i = 1; ; ++i)
    // {
    //     if(i * 6 % MOD == 1)
    //     {
    //         cout << i << endl;
    //         return 0;
    //     }
    // }
    
    cin >> n;
    
    LL res = 0;
    for(int i = 1; i <= n;)
    {
        int x = n / i, y = n / x;
        res = (res + (calc(y) - calc(i-1)) * x) % MOD;
        i = y + 1;
    }
    
    cout << (res + MOD) % MOD << endl;
    
    return 0;
}

二、爬树的甲壳虫

标签:数学、概率论、数学期望、逆元、快速幂

思路:这道题就是推公式,公式就不推了,说一下最后的结论: r e s = ( r e s + 1 ) ∗ y ∗ ( y − x ) − 1 res = (res+1) * y * (y-x)^{-1} res=(res+1)∗y∗(y−x)−1

题目描述:

示例代码:

cpp 复制代码
#include <cstdio>
#include <iostream>

using namespace std;

typedef long long LL;

const int MOD = 998244353;

int n;

LL qmi(LL a, LL k, int p)
{
    LL res = 1;
    while(k)
    {
        if(k & 1) res = res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return res;
}

int main()
{
    scanf("%d", &n);
    
    LL res = 0;
    while(n--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        
        res = (res + 1) * y % MOD * qmi(y-x, MOD-2, MOD) % MOD;
    }
    
    cout << res << endl;
    
    return 0;
}

三、改变数组元素

标签:差分

思路:这道题用暴力做的话就能过 8 / 10 8/10 8/10 的样子,然后再细看这道题就是对一段下标操作,如果操作过就是 1 1 1 ,没有操作过就是 0 0 0 ,那么可以用差分的思想,对于操作过的一段,可以直接加一,代表操作的次数,最后如果操作的次数不是 0 0 0 ,那么就输出 1 1 1 就行了。

题目描述:

cpp 复制代码
给定一个空数组 V 和一个整数数组 a1,a2,...,an。

现在要对数组 V 进行 n 次操作。

第 i 次操作的具体流程如下:

从数组 V 尾部插入整数 0。
将位于数组 V 末尾的 ai 个元素都变为 1(已经是 1 的不予理会)。
注意:
ai 可能为 0,即不做任何改变。
ai 可能大于目前数组 V 所包含的元素个数,此时视为将数组内所有元素变为 1。
请你输出所有操作完成后的数组 V。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 n。
第二行包含 n 个整数 a1,a2,...,an。

输出格式
每组数据输出一行结果,表示所有操作完成后的数组 V,数组内元素之间用空格隔开。

数据范围
1≤T≤20000,1≤n≤2×105,0≤ai≤n,保证一个测试点内所有 n 的和不超过 2×105。

输入样例:
3
6
0 3 0 0 1 3
10
0 0 0 1 0 5 0 0 0 2
3
0 0 0
输出样例:
1 1 0 1 1 1
0 1 1 1 1 1 0 0 1 1
0 0 0

示例代码:

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int N = 2e5+10;

int T, n;
int b[N];

void insert(int l, int r)
{
    b[l]++, b[r+1]--;
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    
    cin >> T;
    while(T--)
    {
        cin >> n;
        memset(b, 0, sizeof b);
        for(int i = 1; i <= n; ++i)
        {
            int t;
            cin >> t;
            if(t == 0) continue;
            else if(t > i) 
            {
                insert(1, i);
            }
            else
            {
                insert(i-t+1,i);
            }
        }
        
        for(int i = 1; i <= n; ++i) 
        {
            b[i] += b[i-1];
            if(b[i]) cout << 1 << " ";
            else cout << 0 << " ";
        }
        cout << endl;
    }
    
    return 0;
}
相关推荐
只做开心事43 分钟前
C++之红黑树模拟实现
开发语言·c++
程序员老冯头2 小时前
第十五章 C++ 数组
开发语言·c++·算法
程序猿会指北3 小时前
【鸿蒙(HarmonyOS)性能优化指南】启动分析工具Launch Profiler
c++·性能优化·harmonyos·openharmony·arkui·启动优化·鸿蒙开发
AC使者6 小时前
5820 丰富的周日生活
数据结构·算法
cwj&xyp7 小时前
Python(二)str、list、tuple、dict、set
前端·python·算法
无 证明7 小时前
new 分配空间;引用
数据结构·c++
xiaoshiguang311 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡11 小时前
【C语言】判断回文
c语言·学习·算法
别NULL11 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇11 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯