1.前缀和:
求一段区间的和
预处理的复杂度是O(n)
访问的复杂度是O(1)
s
[
i
]
=
s
[
i
−
1
]
+
a
[
i
]
s[i]=s[i-1]+a[i]
s[i]=s[i−1]+a[i]
s
[
1
]
=
s
[
0
]
+
a
[
1
]
s[1]=s[0]+a[1]
s[1]=s[0]+a[1]
最大子段和
题目描述
给出一个长度为 n n n 的序列 a a a,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个整数,表示序列的长度 n n n。
第二行有 n n n 个整数,第 i i i 个整数表示序列的第 i i i 个数字 a i a_i ai。
输出格式
输出一行一个整数表示答案。
样例 #1
样例输入 #1
7
2 -4 3 -1 2 -4 3
样例输出 #1
4
提示
样例 1 解释
选取 [ 3 , 5 ] [3, 5] [3,5] 子段 { 3 , − 1 , 2 } \{3, -1, 2\} {3,−1,2},其和为 4 4 4。
数据规模与约定
- 对于 40 % 40\% 40% 的数据,保证 n ≤ 2 × 1 0 3 n \leq 2 \times 10^3 n≤2×103。
- 对于
100
%
100\%
100% 的数据,保证
1
≤
n
≤
2
×
1
0
5
1 \leq n \leq 2 \times 10^5
1≤n≤2×105,
−
1
0
4
≤
a
i
≤
1
0
4
-10^4 \leq a_i \leq 10^4
−104≤ai≤104。
我们要求绿色的区块,绿色的区块的值是:蓝色的区块-黄色的区块
#include <bits/stdc++.h>
using namespace std;
int a[1000005],sum[1000005],summin[1000005],n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i]; //前缀和
}
for(int i=1;i<=n;i++)
{
summin[i]=min(summin[i-1],sum[i]);
}
int res=-2e9; //a数组的最大值是负数
for(int i=1;i<=n;i++)
{
res=max(res,sum[i]-summin[i-1]); //找
}
cout<<res<<endl;
return 0;
}
2.差分:
能多次修改一段区间上的值最后求整个区间的值
原理:
a
1
=
b
1
{a_1}={b_1}
a1=b1
a
2
=
b
1
+
b
2
{a_2}={b_1}+{b_2}
a2=b1+b2
a
3
=
b
1
+
b
2
+
b
3
{a_3}={b_1}+{b_2}+{b_3}
a3=b1+b2+b3
a
4
=
b
1
+
b
2
+
b
3
+
b
4
{a_4}={b_1}+{b_2}+{b_3}+{b_4}
a4=b1+b2+b3+b4
a
n
=
b
1
+
b
2
+
.
.
.
.
.
.
+
b
n
{a_n}={b_1}+{b_2}+......+{b_n}
an=b1+b2+......+bn
想让第2项到第100项都加1:
b
2
{b_2}
b2+=
1
1
1,
b
1
01
{b_101}
b101-=
1
1
1
想让第i项到第r项加d:
b
l
{b_l}
bl+=d,
b
r
+
1
{b_{r+1}}
br+1-=
d
d
d
构造差分:
初始:
a
1
、
a
2
、
a
3
、
a
3
、
a
4
、
.
.
.
.
.
.
、
a
1000
、
.
.
.
.
.
.
、
a
n
{a_1}、{a_2}、{a_3}、{a_3}、{a_4}、......、{a_{1000}}、......、{a_n}
a1、a2、a3、a3、a4、......、a1000、......、an
a
:
a:
a:-5 6 0 0 0 0
b
:
b:
b:-5 11 -6 0 0 0
语文成绩
题目背景
语文考试结束了,成绩还是一如既往地有问题。
题目描述
语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行。她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少。你能帮帮她吗?
输入格式
第一行有两个整数 n n n, p p p,代表学生数与增加分数的次数。
第二行有 n n n 个数, a 1 ∼ a n a_1 \sim a_n a1∼an,代表各个学生的初始成绩。
接下来 p p p 行,每行有三个数, x x x, y y y, z z z,代表给第 x x x 个到第 y y y 个学生每人增加 z z z 分。
输出格式
输出仅一行,代表更改分数后,全班的最低分。
样例 #1
样例输入 #1
3 2
1 1 1
1 2 1
2 3 1
样例输出 #1
2
提示
对于 40 % 40\% 40% 的数据,有 n ≤ 1 0 3 n \le 10^3 n≤103。
对于 60 % 60\% 60% 的数据,有 n ≤ 1 0 4 n \le 10^4 n≤104。
对于 80 % 80\% 80% 的数据,有 n ≤ 1 0 5 n \le 10^5 n≤105。
对于 100 % 100\% 100% 的数据,有 n ≤ 5 × 1 0 6 n \le 5\times 10^6 n≤5×106, p ≤ n p \le n p≤n,学生初始成绩 $ \le 100 , , ,z \le 100$。
#include<bits/stdc++.h>
using namespace std;
int d[5000001];
int a[5000001];
int main()
{
int n,p,x,y,z,i,min=1e9; //min的初始值一定要设的很大
cin>>n>>p;
for(i=1;i<=n;i++)
{
cin>>a[i];
}
for(i=1;i<=n;i++)
{
d[i]=a[i]-a[i-1]; //前缀和
}
for(i=0;i<p;i++)
{
cin>>x>>y>>z; //差分
d[x]+=z;
d[y+1]-=z;
}
for(i=1;i<=n;i++)
{
a[i]=a[i-1]+d[i]; //转换
if(min>a[i]) //找最小值
{
min=a[i];
}
}
cout<<min;
return 0;
}
逛画展
题目描述
博览馆正在展出由世上最佳的 m m m 位画家所画的图画。
游客在购买门票时必须说明两个数字, a a a 和 b b b,代表他要看展览中的第 a a a 幅至第 b b b 幅画(包含 a , b a,b a,b)之间的所有图画,而门票的价钱就是一张图画一元。
Sept 希望入场后可以看到所有名师的图画。当然,他想最小化购买门票的价格。
请求出他购买门票时应选择的 a , b a,b a,b,数据保证一定有解。
若存在多组解,输出 a a a 最小的那组。
输入格式
第一行两个整数 n , m n,m n,m,分别表示博览馆内的图画总数及这些图画是由多少位名师的画所绘画的。
第二行包含 n n n 个整数 a i a_i ai,代表画第 i i i 幅画的名师的编号。
输出格式
一行两个整数 a , b a,b a,b。
样例 #1
样例输入 #1
12 5
2 5 3 1 3 2 4 1 1 5 4 3
样例输出 #1
2 7
提示
数据规模与约定
- 对于 30 % 30\% 30% 的数据,有 n ≤ 200 n\le200 n≤200, m ≤ 20 m\le20 m≤20。
- 对于 60 % 60\% 60% 的数据,有 n ≤ 1 0 5 n\le10^5 n≤105, m ≤ 1 0 3 m\le10^3 m≤103。
- 对于
100
%
100\%
100% 的数据,有
1
≤
n
≤
1
0
6
1\leq n\le10^6
1≤n≤106,
1
≤
a
i
≤
m
≤
2
×
1
0
3
1 \leq a_i \leq m\le2\times10^3
1≤ai≤m≤2×103。
这题要用双指针!
第一步:找到第一个r的位置,从 a [ 1 ] a[1] a[1] ~ a [ r ] a[r] a[r] m m m个
#include <bits/stdc++.h>
int m[2001],num;
int n[1000000];
int main()
{
int R=-1,L=0,N,M,t,i=0,ansL,ansR;
scanf("%d%d",&N,&M);
for(i=0;i<N;i++)
{
scanf("%d",n+i);
}
i=0;
while(num!=M)
{
if(m[n[i]]==0)num++; //装桶
m[n[i]]++;
R++; //找r的值
i++;
}
while(m[n[L]]>1)
{
m[n[L++]]--;
}
ansL=L;ansR=R;
while(i<N)
{
m[n[i]]++;
R++;
i++;
while(m[n[L]]>1)
m[n[L++]]--;
if(ansR-ansL>R-L)
{
ansR=R;
ansL=L;
}
}
printf("%d %d",ansL+1,ansR+1);
return 0;
}