洛谷 8649.k倍区间

这道题是前缀和和一点数论的运用,看到连续子段和第一时间想到的就是前缀和的解法,而且对于竞赛题而言,一般数据范围很大。所以我们都需要优化算法才能让很大的数据范围有个着落。

这里的数据范围我们可以看到是O(n)的时间复杂度。虽然我们在OI赛制中直接可以暴力得出一部分的样例分,但是对于我们的算法能力提升是微乎其微的。所以我们要进行优化。

这里给大家科普一个有些人不知道的数论知识,就是同余定理了。

同余定理_百度百科 (baidu.com)大家可以了解一下,这里我们直接在运用中去讲解。

思路:首先想到就是用前缀和的知识进行操作,让我们求区间的和与k的倍数关系。那么我们可以想,假如我们求区间[l,r],那么这个区间肯定就是al+al+1+...+ar的值,这里我们用Si来代表前缀和,简单来说就是让我们求Sr-Sl的值是k的倍数的区间有多少个的问题而已。(因为这里l,r都可以是0,所以不是Sr-Sl-1)。由同余定理我们知道,如果说(a-b)%c=0,那么也就是说像a%c=b%c的形式。也就是说,Sr%k=Sl%k这样的符合条件的有多少个区间。这就要求Sl和Sr的余数是否相等的问题了。所以,当我们选取个数的时候,需要从相同余数的区间当中任选两个,也就是数学里面的组合数计算了(这个计算可以看高斯公式,也是高中的知识点,不会的话就去查吧),而对于余数不同的区间我们可以怎么办呢?

这就需要提到我们所知道的数据结构中的一个排序问题的方法了,也是个稳定排序算法,就是基数排序问题。在基数排序中,我们会用到类似于桶排序的方法存储余数进行排序。这里我们借用桶排序的思想,利用桶来存储余数,这样我们就可以解决余数不相同的个数怎么存储的问题了。

那么接下来,就一步一步阐述代码的编写。

注意:如果不把握数据范围的话直接全写long long就行了,要不然会有一些很bug的错误出现。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath> 
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<sstream>
#include<map>
#include<limits.h>
#include<set>
#define MAX 100005
#define _for(i,a,b) for(int i=a;i<(b);i++)
#define ALL(x) x.begin(),x.end()
using namespace std;
typedef long long LL;
LL a[MAX];//前缀和数组
LL tong[MAX];//这个就是桶的数组,用来存储余数是i的前缀和的个数
LL n;
void qianzhuihe(){
    _for(i,1,n+1)
    a[i]+=a[i-1];
}//前缀和的求解
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);//加快输入输出
    LL k;
    cin >> n >> k;
    LL sum = 0;
    tong[0]=1;//这里之所以需要为1是因为S0是存在的,而通常我们算前缀和是不算进它的,需要首先为赋值
    _for(i,1,n+1)
    cin>>a[i];
    qianzhuihe();
    _for(i, 1, n+1)
    {
        sum=a[i]%k;//对前缀和求余数
        tong[sum]++;//存储余数是此的区间个数
    }
    LL count = 0;//统计总个数
    _for(i, 0, k) {
        count += (tong[i] * (tong[i] - 1)) / 2;//组合数计算
    }
    cout << count << endl;
    return 0;
}

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值