K区间:土豆片der神奇想法

 神奇的一题由神奇的熊熊提供灵感!!!


题目:

        题目描述:

        有一个神奇的区间名称称为k区间,为什么叫k区间呢,因为这个区间内所有数的和是k的倍数,现在给你一个序列 ,现在需要你求出有多少个k区间。

        输入描述:

                第一行一个整数n ,和一个整数k ( 1 <= n <= 1000000 , 1 <= k <= 100);
                第二行是n个整数 ai, … an ( 1 <= ai <= 100);

        输出描述:

                输出一个整数 表示有多少个k区间。

         用例输入:

                5 3 

                1 2 3 4 5

        用例输出:

                7


思考:

土豆片感觉比较正确的思路:

         一个区间要想是k的倍数,那么肯定取余k是等于0,所以我们理所当然可以想到万能做法———暴力!!!只需要简单的两个for循环就可以非常容易的找到取余k等于0,也就是所有数的和是k的倍数的区间数量,最后用幼儿园学过的加法就可以得到最后的答案!!!

        但是这时候白熊看到了土豆片的无脑暴力代码非常无语,并让他滚回去好好看看时间复杂度,这时土豆片才突然发现1 <= n <= 1000000,时间复杂度竟然有1e6*1e6!土豆片直接醒悟:如此大的数据暴力是会被玩坏的!

土豆片的再次思考:

        既然不能暴力那就需要更高级的做法,需要区间的和,那就很容易想到我们在处理区间问题里所使用的算法(可能算是?)——前缀和!先使用前缀和对数组进行处理,然后找出前缀和模k的值为0,1,2…k-1的区间数分别求出来,然后分别计算,这里需要用到胎教学过的排列组合的知识(应该不会有人不会吧)把每个前缀和取余k等于零的值的情况累加就能得到答案!!!

        例:前缀和模k为1的区间有x个。相当于x个中选两个有几种选法,那么就可以产生C(x,2)个k倍区间(即x(x-1)/2个)。前缀和模k相同的区间中任意挑两个区间可产生一个k倍区间。

有了以上思考,土豆片轻(难)轻(的)松(一)松(批)就写出来以下代码并且有自信一发a掉这题。

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

int n,k,sum[1000005],a[1000005],b[1000005],ans;

signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];   	//前缀和 
	}
	for(int i=1;i<=n;i++){
		b[sum[i]%k]++;			//桶排思想记录每种的数量 
	}
	for(int i=0;i<k;i++){
		ans+=b[i]*(b[i]-1)/2;   //简单的排列组合知识 
	}
	cout<<ans;
}

   但是!!土豆片发现自己写出来的代码连样例都没过!土豆片直接红温!

到底是哪里出了问题?

土豆片的最终思考:

土豆片感觉已经将所有都考虑到了竟然还没有ac,在认真思考了一坤分钟以后,土豆片突然想到,现在计算的都是取余k以后区间和相同的区间,是需要两两配对找到这一类的k区间数量,但是如果取余后区间和为0的话他本身就是一个k区间了,不需要进行两两配对,只需要最后把前缀和取余等于0的个数直接加上就行了!可以对以上代码进行简单修改:

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

int n,k,sum[1000005],a[1000005],b[1000005],ans;

signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];   	//前缀和 
	}
	for(int i=1;i<=n;i++){
		b[sum[i]%k]++;			//桶排思想记录每种的数量 
	}
	for(int i=0;i<k;i++){
		ans+=b[i]*(b[i]-1)/2;   //简单的排列组合知识,任选两个即可组成一个k区间 
	}
	ans+=b[0];
	cout<<ans;
}

样例过了就是过了,土豆片直接提交!

                                                

完美结束!

       

总结:

                核心思想:

                        1.对于区间问题要联想到前缀和与差分来进行先处理。

                        2.两个前缀和模k的值相同的区间,可以产生一个k倍区间。(注意0的情况)

                        3.桶排的类似思想来记录每种情况出现的次数。

                        4.排列组合知识找到最后答案。      

撒花撒花~*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bluechips·zhao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值