C - 前缀极差
2023HUEL寒假集训: 1.10 前缀和 - Virtual Judge (vjudge.net)
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 1e5 + 100;
int a[N];
int p[N], t[N];
int x[N];
int main()
{
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
if (i == 1)
p[i] = a[i], t[i] = a[i];
else
p[i] = max(p[i - 1], a[i]),
t[i] = min(t[i - 1], a[i]);
}
for (int i = 1; i <= q; i++)
{
scanf("%d", &x[i]);
x[i] = p[x[i]] - t[x[i]];
}
for (int i = 1; i < q; i++)
printf("%d ", x[i]);
printf("%d", x[q]);
return 0;
}
P8649 [蓝桥杯 2017 省 B] k 倍区间
P8649 [蓝桥杯 2017 省 B] k 倍区间 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
使用前缀和取模。
我们假设n=5;k=2;
输入的5个数为1,2,3,4,5的话
程序输出6 这6种情况分别是123 1234 2345 345 2 4;
若我们将前i项和的模存到一个数组mod[i]中,mod[1]表示前一项的和的模,mod[2]表示前两项的和的模,以此类推,再将mod[i]相同的个数用一个数组add[mod[i]]存起来,只需要计算C 2 add[i](高中数学的排列组合,2在上面,add[i]在下面)再加上add[0]的所有结果,便可求得正确答案,极大简化了运算时间。
还是上面那个例子:1 2 3 4 5 假如是输入这5个数
可得mod[1]=1(1%2=1) mod[2]=1((1+2)%2=1) mod[3]=0 mod[4]=0 mod[5]=1
mod=1的个数有3个,mod=0的个数有2个,所以add[0]=2,add[1]=3;
我们先看mod=0的时候,所对应的数是3,4,在(3,4]区间中(注意是左开右闭),4%2=0满足条件,
mod=1的时候,所对应的数是1,2,5,在(1,2]区间中,2%2=0满足条件,(1,5]区间中,2345满足条件(2+3+4+5)%2=0,在(2,5]区间中,345满足条件。
所以可以得出结论,从每一种前缀和(所选两个数的中间的数的和)的情况中任意选择两个数组成的区间都满足是k倍区间,其计算方法其实就是排列组合,上面的例子即可表示为C2 2加上C2 3结果为1+3=4,但实际结果有6种,是由于区间是左开右闭的,加上add[0]的所有结果也就是2和4两种,相当于区间左闭右闭,这两个数本身构成一个k倍区间,4+2=6,刚好是正确答案。
于是可得如下代码:
#include <iostream>
using namespace std;
long long mod[100010],add[100010];
int main()
{
int n,k,a;
long long cnt=0;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a;
mod[i]=(mod[i-1]+a)%k;
add[mod[i]]++;
}
for(int i=0;i<n;i++)
{
cnt+=add[i]*(add[i]-1)/2;//排列组合
}
cout<<cnt+add[0];
return 0;
}
P6568 [NOI Online #3 提高组] 水壶
P6568 [NOI Online #3 提高组] 水壶 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
AC Code:
#include <iostream>
using namespace std;
const int N = 1e6 + 5;
int a[N], sum[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
sum[i] = sum[i - 1] + a[i];
}
int max = 0;
for (int i = 1; i + m <= n; i++)
{
int k = sum[i + m] - sum[i - 1]; // 前缀和计算,注意,水壶倒的次数比子段长度小1(自己想想为什么)
if (k > max)
max = k; // 判断大小
}