AcWing 1230. (小白解释为什么cnt[0]=1)K倍区间
原题链接
中等
作者:
Safe_Sound
,
2020-10-07 11:25:49
,
所有人可见
,
阅读 5498
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
int n,k;
LL A[N];
LL s[N],cnt[N];//cnt计算相同余数的序列的个数总和
LL res;//计算不相等的余数的个数总和
int main(){
scanf("%d %d",&n,&k);
//为什么赋值为1?
//因为我们的思路是找两个序列和a%k和b%k的余数相同的个数
//而我们的前缀和一般是不包含S0这个东西的,因为没有意义,但是这道题有意义
//样例里面前缀和序列%k之后是1 1 0 0 1,两两比较,我们只能找到四个
//为什么少了两个?因为我们不一定需要两个序列,单个序列取余=0也构成K倍区间
//也就是说s3 s4这两个区间是能单独成K倍区间的,而我们的思路是找两个序列比较
//此时,我们就要假设S0=0是有意义的,我们就可以有(s0,s3),(s0,s4)这两个组合
cnt[0] = 1;
for(int i = 1; i <= n; i ++){
scanf("%lld",&A[i]);
s[i] = s[i - 1] + A[i];
s[i] = s[i] % k;
res += (cnt[s[i]]);
cnt[s[i]] ++;//记录序列%k不同余数的个数
//cnt[s[i]]要在res之后做,因为它是记录总数,如果下面再加回去会重复。
/*不能写成
cnt[s[i]]++;
res+=(cnt[s[i]]);
*/
//用笔写字调试一下就知道了
}
//cnt[0] = 1;
/*for(int i = 1; i <= n; i ++){
/*for(int j = i; j <= n; j ++){
if(s[j] == 0 && j > 0) break;
if((s[j] - s[i - 1]) % k == 0){
res ++;
}
}
改成一重循环可以从这里删去。
这段代码的含义是: 当j固定时,在1-R之间,
找到有多少个L,满足([S[右端点] - S[左端点])%k == 0
从含义上优化代码
空间换时间
*/
//res += (cnt[s[i]]);
/*我刚开始迷惑为什么不是当cnt[s[i]]>=2的时候才加,
现在懂了,因为不一定是两个区间的重叠序列构成K倍区间,
一个序列本身如果连续和%k=0也构成K倍区间。故所有的都要加上
*/
/*10^10>10^8 TLE -> 要改为一重循环*/
printf("%lld\n",res);
return 0;
}
nb,意思就是说不置为1的话,那么就少了自身单独一个就可以成立的情况
自身单独一个的情况不是前缀和序列%k之后两个数相同且相邻的情况吗?置1是从头到 i的情况吧
cnt保存的是余数的个数 cnt[ i ] 的意思是前面部分余数为 i 的数的个数,但是当 i = 0时,它自身就符合题意,所以这里就有了两种做法:
(1)在最后printf( res + cnt[ 0 ] ),即补上未计算到的单个的 0 的数目
(2)自己一开始就凑一个 cnt[ 0 ] = 1,即自己凑一个来防止单个0不成对的情况
对对对
赞
tql
cnt下标是前缀和模k,而不是单个数
解释的太好辣
%%% 太厉害了!!
orz
orz
证明Cn2 + n == Cn+1 2,就知道为什么是1了
orz
nb
同余是不包括两个一样的数%k吗,(4+4)% k这样的
cnt[s[i]]++; //记录序列%k不同余数的个数
不是这个意思吧。cnt[s[i]]++
的意思应该是将余数为s[i]
的个数更新(加1)你说的应该比较合理~
太强了大佬,把我的疑问点都解释了
dalao很好的解答了我的疑惑 但是能不能 分别判断一下原数组每个数单独能否%k==0呢
就加个ans+=(!a[i]%k)
你可以试试 我都忘了 哈哈哈哈哈
tql 大佬Orz
%%%
orz!!!!
女少啊
…答案有一个评测点过不了蓝桥杯的系统,有大佬看看哪里错了吗,acwing能过
哪里啊