【ACM算法竞赛日常训练】DAY16【奇♂妙拆分】【区区区间间间】【小AA的数列】数学 | 位运算 | 前缀和

DAY16共3题:

  • 奇♂妙拆分(简单数学)

  • 区区区间间间(单调栈)

  • 小AA的数列(位运算dp)

? 作者:Eriktse
? 简介:19岁,211计算机在读,现役ACM银牌选手?力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流C++/Python算法。(优质好文持续更新中……)?
? 阅读原文获得更好阅读体验:https://www.eriktse.com/algorithm/1119.html

奇♂妙拆分(简单数学)

根据贪心的想法,若要使得因子尽可能多,那么因子应当尽可能小,大于根号n的因子至多一个,从小到大枚举[1, sqrt(n)]的所有整数,如果i能够整除n就作为一个因子。

Code:

#include <bits/stdc++.h>
#define int long long
using namespace std;

void solve()
{
    int n;cin >> n;
    set<int> st;
    for(int i = 1;i <= n / i; ++ i)
        if(n % i == 0)st.insert(i), n /= i;
    st.insert(n);
    
    cout << st.size() << '\n';
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int _;cin >> _;
    while(_ --)solve();
    return 0;
}

区区区间间间(单调栈)

题意:给定一个数组,求所有子区间的最大值与最小值的差的和。

如果暴力枚举肯定不行,单单是子区间个数就有n ^ 2个,所以我们应该考虑每一个元素对答案的贡献,也就是说我们需要知道某个元素a[i]它会在多少个区间里作为最大值,在多少个区间里作为最小值

我们预处理出四个数组,分别是lmax[], rmax[], lmin[], rmin[]表示点i作为最大值的左右最远位置,和作为最小值的左右最远位置(lmax[i] = j,表示在区间[j, i]中的最大值都是a[i],其他的三个数组类似定义)。

用单调栈可以处理出这四个数组,一下以求lmax[]举例,维护一个单调不增栈,栈内存放的是下标

初始时栈内仅有一个a[0] = inf

当我们遇到一个a[i]时,不断地判断栈顶元素,如果栈顶元素比a[i]小,说明这个栈顶应当弹出。

当更新完毕后,此时的栈顶的右边相邻位置就是a[i]往左的最远的位置了,因为此时栈顶是a[i]往左的第一个大于等于a[i]的位置,那么这中间的元素都会小于a[i]

其他的三个数组类似,注意,如果我们处理lmax用的是单调不增栈,那么处理rmax就应当用单调递增栈,而不是单调不减栈,否则可能出现区间重复表示或没有归属的问题(自己思考)。

Code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 9, inf =8e18;
int a[N], stk[N], top, lmax[N], rmax[N], lmin[N], rmin[N];

void solve()
{
    int n;cin >> n;
    for(int i = 1;i <= n; ++ i)cin >> a[i];
    a[0] = a[n + 1] = inf;
    
    top = 0;
    stk[++ top] = 0;
    for(int i = 1;i <= n; ++ i)
    {
        while(top && a[i] > a[stk[top]])top --;
        lmax[i] = stk[top] + 1;
        stk[++ top] = i;
    }
    
    top = 0;
    stk[++ top] = n + 1;
    for(int i = n;i >= 1; -- i)
    {
        while(top && a[i] >= a[stk[top]])top --;
        rmax[i] = stk[top] - 1;
        stk[++ top] = i;
    }
    
    
    a[0] = a[n + 1] = -inf;
    top = 0;
    stk[++ top] = 0;
    for(int i = 1;i <= n; ++ i)
    {
        while(top && a[i] < a[stk[top]])top --;
        lmin[i] = stk[top] + 1;
        stk[++ top] = i;
    }
    
    top = 0;
    stk[++ top] = n + 1;
    for(int i = n;i >= 1; -- i)
    {
        while(top && a[i] <= a[stk[top]])top --;
        rmin[i] = stk[top] - 1;
        stk[++ top] = i;
    }
    int ans = 0;
    for(int i = 1;i <= n; ++ i)
    {
        ans += a[i] * (rmax[i] - i + 1) * (i - lmax[i] + 1);
        ans -= a[i] * (rmin[i] - i + 1) * (i - lmin[i] + 1);
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int _;cin >> _;
    while(_ --)solve();
    return 0;
}

小AA的数列(位运算 | 前缀和)

这道题一看有点懵,感觉不是很好处理,但依然是套路题。

看到异或,我们应该想把每一个二进制位拆分开,实际上这里约32位二进制位可以先认为是相互独立的,对于每一位都计算出贡献,然后按照位权重加和即可。

异或题的套路基本都是拆分二进制,整体考虑起来比较复杂,拆分后会简单很多。

先不管L, R

假如我们考虑数组1 2 3 4 5的第0位:

获取第0位后得到b数组:1 0 1 0 1

因为我们要得到区间内1的个数,所以处理一个异或前缀和p[]是自然而然的,然后我们枚举每一位作为左端点,看看会得到一个怎样的式子:

\[sum=\sum_{j=i + 1}^{j += 2, j \le n}p[j]\oplus p[i-1]
\]

我们知道异或其实就是% 2的加法,也是% 2减法,所以我们将异或前缀和改为普通前缀和p[],以上的柿子可以转化为:

\[sum=\sum_{j=i + 1}^{j += 2, j \le n}[(p[j] - p[i-1]) (mod 2)]
\]

那么我们就可以对p再做一个前缀和p2,但是p2的步长应为2。

然后上面的柿子就等价于(其中l, rj的上下限,需要保证与i - 1奇偶性相同,j的个数为(r - l + 2) / 2):

\[sum=| p2[r] - p2[l - 1] - p[i - 1] * ((r - l + 2) / 2))|
\]

因为这个p2存在p2[-1]的情况,所以我们做个小小的便宜,方便代码的编写(其实你想要特判也行,只是不太方便,也容易出错)。

注意一下其他细节即可。

Code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 109, p = 1e9 + 7, T = 100;
int a[N], b[N], p1[N], p2[N];


signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, l, r;cin >> n >> l >> r;
    for(int i = 1;i <= n; ++ i)cin >> a[i];
    
    int ans = 0;
    for(int w = 0;w <= 32; ++ w)
    {
        for(int i = 1;i <= n; ++ i)b[i] = a[i] >> w & 1;
        for(int i = 1;i <= n; ++ i)p1[T + i] = (b[i] + p1[T + i - 1]) % 2;
        for(int i = 1;i <= n; ++ i)p2[T + i] = p1[T + i] + p2[T + i - 2];
        
        int sum = 0;
        for(int i = 1;i <= n; ++ i)
        {
            int j1 = max(i + 1, l + i - 1), j2 = min(n, r + i - 1);
            if((j1 - i) % 2 == 0)j1 ++;
            if((j2 - i) % 2 == 0)j2 --;
            if(j2 < j1)continue;
            
            sum += abs(p2[T + j2] - p2[T + j1 - 2] - 
                       p1[T + i - 1] * ((j2 - j1) / 2 + 1));
        }
        ans = (ans + (1ll << w) * sum % p) % p;
    }
    cout << ans << '\n';
    return 0;
}

? 本文由eriktse原创,创作不易,如果对您有帮助,欢迎小伙伴们点赞?、收藏⭐、留言?

原文链接:https://www.cnblogs.com/eriktse/p/17335906.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:【ACM算法竞赛日常训练】DAY16【奇♂妙拆分】【区区区间间间】【小AA的数列】数学 | 位运算 | 前缀和 - Python技术站

(0)
上一篇 2023年4月20日
下一篇 2023年4月20日

相关文章

  • Python利用正则表达式实现计算器算法思路解析

    以下是关于“Python利用正则表达式实现计算器算法思路解析”的完整攻略: 简介 计算器是一种常用的工具,用于进行数学运算。在本教程中,我们将介绍如何使用Python和正则表达式实现一个简单的计算器,包括解析表达式、计算结果等步骤。 原理 计算器的实现原理包括解析表达式、转换为逆波兰表达式、计算结果等步骤。在本教程中,我们将使用正则表达式实现表达式的解析,将…

    python 2023年5月14日
    00
  • 字典树的基本知识及使用C语言的相关实现

    字典树的基本知识 字典树,英文名为Trie树,又称单词查找树或键树,是一种树形数据结构,用于表示关联数组或映射。它的优点是,可以大大减少无谓的字符串比较,查询效率比哈希表高。 字典树的核心概念是节点,每个节点包含一个字符和指向子节点的指针。根节点为空字符,每个字符串以一个独立的路径插入节点。如果一个字符串是另一个字符串的前缀,那么这个字符串的节点是另一个字符…

    数据结构 2023年5月17日
    00
  • Python实现七个基本算法的实例代码

    下面是关于“Python实现七个基本算法的实例代码”的完整攻略。 1. 七个基本算法 七个基本法是指排序、查找、字符串、数组、表、树图这七个领域的基本算法。这些算法是计算机科学最基本的算法之一,也是Python开发者必须握的算法之一。 2. 算法实现 下面是使用Python实现七个基本算法的完整代码。 2.1 排序算法 2.1.1 冒泡排序 def bubb…

    python 2023年5月13日
    00
  • Java 数据结构链表操作实现代码

    下面是关于“Java 数据结构链表操作实现代码”的完整攻略。 1.链表实现原理 链表是一种经典的数据结构,其主要原理是通过指针将一系列节点连接起来。链表中的节点包含两个部分,一个是数据域,用于存放数据;另一个是指针域,用于指向下一个节点的位置。链表的头结点指向链表的第一个节点,最后一个节点的指针指向空。 2.链表的基本操作 链表的基本操作包括创建链表、插入节…

    数据结构 2023年5月17日
    00
  • NTP时间同步服务器(频率同步)包含帧同步、载波同步、位同步

    NTP时间同步服务器(频率同步)包含帧同步、载波同步、位同步 NTP时间同步服务器(频率同步)包含帧同步、载波同步、位同步 京准电子科技官微——ahjzsz 同步的概念   同步技术是数字通信系统中非常重要的技术。一般来说数字通信系统要实现多种同步功能才能实现正确的数据通信任务。其技术目标是实现不同地域收发双方的同步通信互联,实现一致的信息数据交换,因此,通…

    算法与数据结构 2023年4月17日
    00
  • python的自变量选择(所有子集回归,后退法,逐步回归)

    自变量选择是指在建立回归模型时,选择哪些自变量对因变量的影响最大。常用的自变量选择方法包括所有子集回归、后退法和逐步回归。本文将详细介绍这三种方法的实现过程,并提供两个示例说明。 所有子集回归 所有子集回归是一种穷举法,它将所有可能的自变量组合都考虑到,并选择最优的组合。在Python中,我们可以使用mlxtend库中的ExhaustiveFeatureSe…

    python 2023年5月14日
    00
  • Python实现的质因式分解算法示例

    Python实现的质因式分解算法示例 质因式分解是一种将一个正整数分解成若干个质数乘积的方法。在Python中,可以使用多种算法来实现质式分解,包括试除法、分解质因数、Pollard-Rho算法等。本文将详细讲解Python实现的质因式分解算法示例,包括算法原理、实现过程和示例。 算法原理 质因式分解是一种将一个正整数分解成若干个质数乘积方法。具体来说,质因…

    python 2023年5月13日
    00
  • 2020滴滴最新PHP试题(附答案及解析)

    题目链接:https://www.fibar.cn/newsDetail/18216.html 本文主要是对“2020滴滴最新PHP试题(附答案及解析)”的解题思路和过程进行详细讲解。 题目难度 此题属于中等难度,需要考生具备 PHP 基础知识和算法基础。 题目要求 题目要求我们编写一个程序,实现多个字符串的排序输出。程序需要满足以下要求: 输入:多个字符串…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部