Day1 T3 数列

题目

给你一个长度为N的正整数序列,如果一个连续的子序列,子序列的和能够被K整除,那么就视此子序列合法,求原序列包括多少个合法的连续子序列?对于一个长度为8的序列,K=4的情况:2, 1, 2, 1, 1, 2, 1, 2 。它的答案为6,子序列是位置1->位置8,2->4,2->7,3->5,4->6,5->7。

输入

第一行:T,表示数据组数
对于每组数据:
第一行:2个数,K,N
第二行:N个数,表示这个序列

输出

共T行,每行一个数表示答案

样例

输入输出
2
7 3
1 2 3
4 8
2 1 2 1 1 2 1 2
0
6

数据范围

100%数据满足1 \(\leqslant\) T \(\leqslant\) 20,1 \(\leqslant\) N \(\leqslant\) 50000,1 \(\leqslant\) K \(\leqslant\) 1000000,序列的每个数 \(\leqslant\) 1000000000
30%数据满足1 \(\leqslant\) T \(\leqslant\) 10,1 \(\leqslant\) N,K \(\leqslant\) 1000

题解

先跑一遍前缀和,在前缀和的时候别忘了取模,一是会爆,二是后面有用。
如果一个从头开始的子序列的和与另一个从头开始的子序列的和同余,设其分别为\(S_a\)\(S_b\),不妨设\(S_b\)>\(S_a\)
\(S_a≡S_b(mod\ k)\)
\(0≡S_b-S_a(mod\ k)\)
又∵0对任何输取模都为0,
\(S_b-S_a\ mod\ k=0\)
即从a到b的子序列之和能被K整除。
然后统计模相等的从头开始的子序列即可。一对可以产生一个和能被K整除的子序列,n个便能排列出\(\frac{n(n-1)}{2}\)对(小学生都会的排列组合),可以产生\(\frac{n(n-1)}{2}\)能被K整除的子序列。
把sort用桶排序替换,时间复杂度可以降到O(n+k)。
其实就是我懒得写桶排

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int rnd,k,n;
ll cnt=0;
int s[50010],a[50010];

int main(){
    scanf("%d",&rnd);
    while(rnd--){
        memset(a,0,sizeof(a));
        memset(s,0,sizeof(s));
        cnt=0;
        scanf("%d %d",&k,&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            a[i]%=k;
            s[i]=(s[i-1]+a[i])%k;
        }
        sort(s,s+n+1);
        int t=1; 
        for(int i=1;i<=n;i++){
            if(s[i]==s[i-1]) t++;
            else{
                cnt+=t*(t-1)/2;
                t=1;
            }
        }
        cnt+=t*(t-1)/2;
        //如果s[n]=s[n-1],便没有再运行else中的cnt+=t*(t-1)/2,所以在for循环之后还要再写一句 
        printf("%lld\n",cnt);
    }
    return 0;
}

转载于:https://www.cnblogs.com/znk161223/p/11427754.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值