题单:【蓝桥杯】蓝桥杯省一秘诀(国赛题单看简介) - 题单 - 洛谷 | 计算机科学教育新生态
状态:差分没看太懂,前缀和倒是有熟练了一点
P8649 [蓝桥杯 2017 省 B] k 倍区间
题目链接:[P8649 蓝桥杯 2017 省 B] k 倍区间 - 洛谷
题目描述
给定一个长度为 N 的数列,A 1,A 2,⋯A**N ,如果其中一段连续的子序列 A**i ,A**i +1,⋯A**j (i ≤j ) 之和是 K 的倍数,我们就称这个区间 [i ,j ] 是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
输入格式
第一行包含两个整数 N 和 K (1≤N ,K≤105)。
以下 N 行每行包含一个整数 A**i (1≤A**i≤105)。
输出格式
输出一个整数,代表 K 倍区间的数目。
输入输出样例
输入 #1
5 2
1
2
3
4
5
输出 #1
6
说明/提示
时限 2 秒, 256M。蓝桥杯 2017 年第八届
这道题要是模拟的话会超时,依旧使用前缀和的思想来解决。子区间 [i, j] 的和等于 pre[j] - pre[i-1](其中 pre[0]=0,pre[t] 表示前 t 个元素的和)。该和是 K 的倍数当且仅当 (pre[j] - pre[i-1]) % K == 0,即 pre[j] % K == pre[i-1] % K。所以前缀和里存的是前缀和取模后的余数,
#include <bits/stdc++.h>
using namespace std;
int main() {
int N, K;
cin >> N >> K;
vector<long long> cnt(K, 0); // 统计每个余数出现的次数
cnt[0] = 1; // 初始前缀和为0,余数为0
long long ans = 0; //存放最后的结果
long long sum = 0; // 当前前缀和
for (int i = 0; i < N; ++i) {
int x;
cin >> x;
sum = (sum + x) % K; // 更新当前前缀和模K
ans += cnt[sum]; // 加上之前相同余数的个数
cnt[sum]++; // 当前余数出现次数加1
}
cout << ans << endl;
return 0;
}
CF816B Karen and Coffee
题目链接:CF816B Karen and Coffee - 洛谷
题目描述
为了在上课时保持清醒和专注,Karen 需要喝点咖啡!
Karen 是一位咖啡爱好者,她想知道冲泡完美咖啡的最佳温度。实际上,她花了一些时间阅读了几本食谱,包括备受赞誉的《The Art of the Covfefe》。
她一共掌握了 n 种咖啡配方。第 i 个配方建议咖啡应在 l**i 到 r**i 度(包含端点)之间冲泡,以获得最佳口感。
Karen 认为,如果有至少 k 个配方推荐某个温度,则该温度为可接受温度。
Karen 的想法总是变化多端,于是她总共提出了 q 个问题。每个问题,她都会给定一个温度范围 a 到 b,想让你告诉她,在这个范围内有多少个可接受的整数温度。
输入格式
输入的第一行包含三个整数 n , k (1≤k ≤n ≤200000)、q (1≤q≤200000),分别表示配方数量、某个温度被判定为可接受所需的最少配方数,以及 Karen 提出的查询个数。
接下来的 n 行描述每个配方。具体地,第 i 行包含两个整数 l**i 和 r**i (1≤l**i ≤r**i ≤200000),表示第 i 个配方推荐咖啡应在 l**i 到 r**i 度(包含端点)之间冲泡。
接下来的 q 行描述各个问题。每行包含两个整数 a 和 b (1≤a ≤b ≤200000),表示她想知道在 a 到 b(包含端点)度之间有多少个可接受的整数温度。
输出格式
对于每一个问题,输出一行一个整数,表示在 a 到 b 度之间有多少个可接受的整数温度。
输入输出样例
输入 #1
3 2 4
91 94
92 97
97 99
92 94
93 97
95 96
90 100
输出 #1
3
3
0
4
输入 #2
2 1 1
1 1
200000 200000
90 100
输出 #2
0
说明/提示
在第一个测试点中,Karen 掌握了 3 个配方。
-
第一个推荐温度区间为 91 到 94 度(包含端点)。
-
第二个推荐温度区间为 92 到 97 度(包含端点)。
-
第三个推荐温度区间为 97 到 99 度(包含端点)。
只要某个温度区间内至少有 2 个配方推荐,则该温度为可接受温度。
她一共提出了 4 个问题。
在第一个询问中,她想知道在 92 到 94 度之间的可接受整数温度有多少。答案是 3,因为 92, 93, 94 度都是可接受的。
在第二个询问中,她想知道在 93 到 97 度之间的可接受整数温度有多少。答案是 3,因为 93, 94, 97 度都是可接受的。
在第三个询问中,她想知道在 95 到 96 度之间的可接受整数温度有多少。没有可接受的温度。
在最后一个询问中,她想知道在 90 到 100 度之间有多少个可接受的整数温度。答案是 4,分别是 92, 93,94,97 度。
在第二个测试点中,Karen 掌握了 2 个配方。
-
第一个配方建议只在 1 度冲泡咖啡。
-
第二个配方建议只在 200000 度冲泡咖啡。
只要某个温度区间内至少有 1 个配方推荐,则该温度为可接受温度。
她有且仅有一个问题,询问在合理范围内有多少可接受整数温度。实际上没有。
这是一个典型的差分数组 +和前缀和的应用,用于解决区间覆盖计数和区间查询的问题。假设我们有一个数组 cover,长度等于温度范围,初始全为 0。差分数组统计每个温度的覆盖次数。差分数组 diff 是这样定义的:diff[i] 表示 cover[i] 与 cover[i-1] 的差值(即 cover[i] - cover[i-1])。反过来,如果我们知道 diff,就可以通过前缀和还原出 cover:
现在,如果我们想在区间 [l, r] 上每个位置都加 1,这在差分数组上如何体现?
-
在
l位置加 1,会让从l开始的cover都增加 1。 -
为了不让
r+1及以后的位置也增加,我们还要在r+1位置减 1。diff[l] += 1 diff[r+1] -= 1
这样,最后对 diff 求一遍前缀和,就能得到每个温度的实际覆盖次数 cover。
#include<stdio.h>
using namespace std;
int f[200005];
int main(){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
int i;
int max=0;
for(i=1;i<=n;i++){//差分代码
int a,b;
scanf("%d%d",&a,&b);
f[a]+=1;f[b+1]-=1;
if(b>max)max=b;
}
for(i=1;i<=max;i++){
f[i]=f[i-1]+f[i];
}
for(i=1;i<=200000;i++){//前缀和
if(f[i]>=m)f[i]=1+f[i-1];
else f[i]=f[i-1];
}
for(i=1;i<=k;i++){
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",f[r]-f[l-1]);//前缀和计算
}
return 0;
}