[蓝桥杯][2017年第八届真题]k倍区间

暴力的做法是\(O(n^2)\),枚举左右端点\(l\)\(r\),通过前缀和计算出\(sum[l \sim r] = sum[r] - sum[l-1]\),但不足以通过此题。

题意可转化为:找两个端点\(l\)\(r\),使得\(sum[l \sim r]\%k = (sum[r] - sum[l-1])\%k == 0\),即\(sum[r] \equiv sum[l-1] \mod k\)

于是我们统计出所有\(sum[i] \mod k\)的余数,记为\(cnt[sum[i]\%k]\)

最后枚举余数\((0 \sim k-1)\),假设当前枚举的余数为\(m\),则满足\(sum[l \sim r]\%k == m\)\(l\)\(r\)配对的个数为\(C_{cnt[m]}^2\)

注意点

由于\(sum[0]=0\),因此初始时\(cnt[0]=1\)

const int N=1e5+10;
LL sum[N],cnt[N];
int n,k;

int main()
{
    cin>>n>>k;

    cnt[0]=1;
    for(int i=1;i<=n;i++)
    {
        cin>>sum[i];
        sum[i]+=sum[i-1];
        cnt[sum[i]%k]++;
    }

    LL ans=0;
    for(int i=0;i<k;i++)
    {
        ans+=cnt[i]*(cnt[i]-1)/2;
    }
        
    cout<<ans<<endl;

    //system("pause");
    return 0;
}

也可以采用下面更简洁的写法,不再是求组合数了,而是规定了一个顺序,每次求当前为右端点\(r\)的情况下,\(1 \sim i-1\)中与\(sum[r]\)同余模\(k\)的左端点\(l\)的个数。

const int N=1e5+10;
int sum[N],cnt[N];
int n,k;

int main()
{
    cin>>n>>k;

    LL ans=0;
    cnt[0]=1;
    for(int i=1;i<=n;i++)
    {
        cin>>sum[i];
        sum[i]=(sum[i]+sum[i-1])%k;
        ans+=cnt[sum[i]];
        cnt[sum[i]]++;
    }
    cout<<ans<<endl;

    //system("pause");
    return 0;
}
posted @ 2021-03-20 19:34  Dazzling!  阅读(44)  评论(0编辑  收藏  举报