第八届蓝桥杯省赛第十题
前缀和优化后时间N2,对于1e5的数据量依旧超时这里用一个存余数的技巧
AcWing倒师傅的题解
有了这个结论之后,我们就可以使用两层for循环来计数k倍区间的个数,但是由于数据比较大,我们不能这样做。那么我们能不能在计算前缀和的过程中同时来统计k倍区间的个数呢?当然可以。我们可以用一个数组cnt,规定cnt[i]表示当前位置之前,前缀和取模后等于i的个数,以后每出现一次前缀和(取模后)和它相等,那么k倍区间就加上cnt[sum[i]],然后cnt[sum[i]]++。这样似乎不容易理解,我们用样例举个例子。
对于数列 1 2 3 4 5,k = 2
对前1个数的和模k后为1,在此之前有0个前缀和取模后为1,总个数+0
对前2个数的和模k后为1,在此之前有1个前缀和取模后为1,总个数+1
对前3个数的和模k后为0,在此之前有0个前缀和取模后为0, 总个数+0
对前4个数的和模k后为0,在此之前有1个前缀和取模后为0,总个数+1
对前5个数的和模k后为1,在此之前有2个前缀和取模后为1,总个数+2
但是我们还忽略了一点,就是我们这样做我们少计算了区间[0,i][0,i]构成的k倍区间,其个数为cnt[0]。
作者:倒计时0天
链接:https://www.acwing.com/solution/AcWing/content/6889/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(System.out);
static int N = 100010;
static int a[] = new int[N], S[] = new int[N], ans[] = new int[N];
static int n, k;
static long res;
public static void main(String[] args) throws Exception {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
k = Integer.parseInt(s[1]);
for (int i = 1; i <= n; i++) {
a[i] = Integer.parseInt(br.readLine());
S[i] = (S[i - 1] + a[i]) % k;
}
long res = 0;
ans[0] = 1;
for (int i = 1; i <= n; i++) {
res += ans[S[i]];
ans[S[i]]++;
}
pw.println(res);
pw.flush();
pw.close();
br.close();
}
}