zoukankan      html  css  js  c++  java
  • 第八届蓝桥杯省赛 K倍区间

    问题描述
      给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
      你能求出数列中总共有多少个K倍区间吗?
    输入格式
      第一行包含两个整数N和K。(1 <= N, K <= 100000)
      以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
    输出格式
      输出一个整数,代表K倍区间的数目。
    样例输入
    5 2
    1
    2
    3
    4
    5
    样例输出
    6
    数据规模和约定
      峰值内存消耗(含虚拟机) < 256M
      CPU消耗 < 2000ms

     
    思路
       这题暴力复杂度为$O(n^2)$,无法通过所有数据。我提供两种思路来解决这个问题:
      首先求出数组的前缀和并对k取余。前缀和是数组的前j个元素的和$sumlimits_{i = 1}^j {a[i]}$。假设经过求余得到的数组为${ {y_1},{y_2},...,{y_n}}$,比如样例中的$y$数组为{1,1,0,0,1}。
     
      1.对于数组$y$,如果${y_i} = {y_j}(i < j)$,那么$sumlimits_{t = i + 1}^j {{t_i}}$必定是$k$的倍数。设$c_x$表示数组$y$等于$x$的元素个数,但是要特别注意等于0的情况,要让$c_0+1$!!
      答案就是$sumlimits_{i = 0}^{k - 1} {{c_i} imes ({c_i} - 1)/2}$。
     
      2.在计算数组$y$同时,第$i$个元素能和前面的元素构成$c_{y_i}-1$个k倍区间,这样一直计算下去就是答案。
     
      总而言之,这两种思路都是基于余数相同区间和必为k的倍数来实现的。

    AC代码
      
     1 #include <stdio.h>
     2 #include <string.h>
     3 const int maxn = 100000 + 5;
     4 typedef long long LL;
     5 int a[maxn], cnt[maxn];
     6 int n, k;
     7 int main() {
     8     while(scanf("%d%d", &n, &k) == 2) {
     9         int sum = 0;
    10         LL ans = 0;
    11         memset(cnt, 0, sizeof(cnt));
    12         for(int i = 0; i < n; i++) {
    13             scanf("%d", &a[i]);
    14             sum = (sum + a[i] % k) % k;
    15             cnt[sum]++;
    16         }
    17         cnt[0]++;
    18         for(int i = 0; i < k; i++) {
    19             ans += (1LL * cnt[i] * (cnt[i]-1)) / 2;
    20         }
    21         printf("%lld
    ", ans);
    22     }
    23     return 0;
    24 }
     
     如有不当之处欢迎指出!
     
     
     
  • 相关阅读:
    Desert King
    Dropping tests
    01分数规划小结
    简单的数学题
    [HAOI2016]放棋子
    [SDOI2017]数字表格
    诸侯放置
    LJJ爱数数
    车的放置
    [SDOI2014]数表
  • 原文地址:https://www.cnblogs.com/flyawayl/p/8511277.html
Copyright © 2011-2022 走看看