未知:数列——题解

(SDUT3173是一道和它差不多的题,但因为我无法注册,所以题目来源仍写未知,想要提交本题的人也可以去看看)
(另外,题目无法复制,所以我会概括一下题面)
——————————————————
当一个连续子序列内的整数之和恰好是 K 的整数倍数时该子序列合法。求出合法子序列的个数。

Input
输入第一行是一个整数 T,表示有 T 组数据。(T<=20)
每组数据第一行是两个整数 K (1 <= K <= 10^6),N (1 <= N <= 50000)。
接下来的一行包含 N 个整数 Ai (|Ai| <= 10^9)。

Output
对于每组测试数据,输出一行仅包含一个整数,表示 Edward 喜欢的连续子序
列数量。

Sample Input
2
7 3
1 2 3
4 8
2 1 2 1 1 2 1 2

Sample Output
0
6
——————————————
O(n*n)很容易知道怎么做。
O(n)(大概)做法也用到了前缀和(设为f[i]表示1-i的和)。
我们知道一段区间[i,j]的和的等于f[j]-f[i-1]。
我们对此取模k,然后看看是否为0,如果是则是合法。
然而,我们有(a-b)%k=a%k-b%k。
那也就是说,只要a%k=b%k那么这就是一个合法的序列。
所以,我们首先对所有的前缀和取模k,然后化正,之后排序。
剩下的我想也就不用多说了吧。
(注意一点,f[0]也算在排序的范畴内,它为0)
(路由器的代码和上述略有区别,但是思路一致)
(所以请谨慎食用代码)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
int q[50010];
inline int read(){
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
ll suan(ll a){
    return ((a+1)*a)>>1;
}
int main(){
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    int t,w;
    int k,n;
    ll s,ans;
    t=read();
    while(t--){
        k=read();n=read();
        q[0]=read();
        q[0]%=k;
        if(q[0]<0)q[0]=(q[0]+k)%k;
        for(int i=1;i<n;i++){
            w=read();
            q[i]=q[i-1]+w;
            q[i]%=k;
            if(q[i]<0)q[i]=(q[i]+k)%k;
        }
        sort(q,q+n);
        s=1;ans=0;
        q[n]=-1;
        for(int i=1;i<=n;i++){
            if(q[i]==q[i-1])s++;
            else{
                if(q[i-1]!=0)s--;
                ans+=suan(s);
                s=1;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

//偶对了,还有,不要忘了读入优化233

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值