一维前缀和模板
前缀和就相当于数学的前n项和
这里可以手动模拟一下,就很清晰了
#include<bits/stdc++.h>
using namespace std;
const int N = 1000;
int a[N],s[N];
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i ++) //从下标1开始读入
cin >> a[i];
for(int i = 1; i <= n; i ++) //因为是全局变量s[0] = 0,前n项的和等于前n-1项加上第n个数
s[i] = s[i-1] + a[i];
//前缀和的作用是查询更快,时间复杂度O(1)
int l , r;
cin >> l >> r; //第l个数到第r个数之间的区间和
cout << s[r] - s[l-1] << endl;
}
//原理可能讲得不是很清楚,自行查阅吧
P5638 【CSGRound2】光骓者的荣耀//一维例题
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6+10;
long long a[N];
long long n,k,m;
int main(){
ios::sync_with_stdio(false);
cin >> n >> k;
/*这道题思路是说小k可以跳一次长度是K,那么你只需要寻找一段长度是K的最大时间段,然后减去就行了
范围开个long long,防止数据溢出
*/
for(int i = 1; i <= n-1; i ++) //
{ cin >> m;
a[i] = a[i-1] + m;
}
long long ans = 0;
for(int i = k; i < n; i ++) ans = max(ans,a[i] - a[i-k]);
cout << a[n-1] - ans << endl;//总的时间减去长度是k的最大时间段
}
二维前缀和模板
#include<bits/stdc++.h>
using namespace std;
const int N = 10000; //二维前缀和的板子好像是dp推出来的
int a[N][N],s[N][N];
int main()
{ int n,m;
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m; //n行m列的二维矩阵
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
cin >> a[i][j];
//预处理公式
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i-1][j-1];
//查询x1,y1,x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
//查询某个区间的最大值
int ans = -1; //如果全是正数ans设置为-1,如果有负数,那么ans = -0x3f3f3f3f;
for(int x1 = 1; x1 <= n; x1++)
for(int y1 = 1; y1 <= m; y1 ++)
for(int x2 = x1; x2 <= n; x2 ++)
for(int y2 = y1; y2 <= m; y2 ++)
{
if(s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1] > ans)
ans = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
}
//这个查询有一个缺点,时间复杂度高O(n^4) 等我找到方法再更新
return 0;
}
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e3+10;
long long s[N][N],a[N][N];
long long ans = -1e10;
int main(){
int n,m,c;
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//上面的东西是读入加快相当于和scanf,printf速度差不多具体的自行百度哦
cin >> n >> m >> c;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
cin >> a[i][j];
//二维前缀和简单应用
for(int i = 1; i <= n; i ++) //直接套用预处理公式
for(int j = 1; j <= m; j ++)
{ s[i][j] = s[i-1][j] + s[i][j-1] -s[i-1][j-1] + a[i][j];
}
int x,y; //标记左上角的点 套用板子,注意一下小细节
//因为是一个正方形,只需要枚举一个点(x,y),另一个点是(x+c,y+c)
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++) //i+c,j+c;
{ if(i + c - 1<= n && j + c -1 <= m) //保证两个点的范围是小于n和m的
{ if(s[i+c-1][j+c-1] - s[i-1][j+c-1] - s[i+c-1][j-1] + s[i-1][j-1] > ans)
{ ans = s[i+c-1][j+c-1] - s[i-1][j+c-1] - s[i+c-1][j-1] + s[i-1][j-1];
x = i;
y = j;
}
}
}
cout << x << ' ' << y << endl;
return 0;
}
一维差分
差分就是前缀和的逆运算,相当于函数的求导和积分
这个算法可以处理在一个区间范围内加入一个数,在一个区间内删除一个数
给定一个数组在1 2 2 1 2 1 在下标1-3 之间每个数加1,在下标3 -5之间加1,在下标1-6加入1
朴素做法就是加for循环,然后再这个区间内加入,时间复杂度是O(n^2)
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int a[N],b[N];
int main(){
int n,m;
cin >> n >> m;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
b[i] = a[i] - a[i-1]; //一维差分就是这样构造的。
//构造差分数组,时刻牢记前缀和是差分的逆运算,差分数组 累加 就是前缀和
}
while(m--)
{ int l,r,c;
cin >> l >> r >> c;
b[l] += c;
b[r+1] -= c;
}
for(int i = 1; i <= n; i ++)
{
a[i] = b[i] + a[i-1];
cout << a[i] << ' ';
}
return 0;
}
大致意思就是每次从一个站台l到另一个站台r,统计该站台经过的次数,最后比较该站台买卡实惠,还是买票实惠,如何计算经过的次数是本题的重点,利用差分的思想,在差分数组在左边l加1
,在右边的r出减1就行了,此题的差分数组不用构造了,之间默认每个数都是0
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 1e6+10;
LL s[N];
LL a[N];
LL ans,n,m;
int main()
{ cin >> n >> m;
for(int i = 1; i <= m; i ++) cin >> a[i];
for(int i = 1; i < m; i ++) 区间x到y,找到最大值和最小值
{ int x,y;
x = min(a[i],a[i+1]);
y = max(a[i],a[i+1]);
s[x] += 1;
s[y] -= 1;
}
for(int i = 1; i <= n-1; i ++) //累加构造前缀和,时刻牢记差分是前缀和的逆运算
s[i] += s[i-1];
LL ans = 0;
for(int i = 1; i < n; i ++)
{ LL A,B,C; //这里的LL是longlong类型,防止爆int
cin >> A >> B >> C;
LL p1 = A * s[i] ,p2 = B * s[i] + C; //统计每个数的最优惠价格
ans += min(p1,p2);
}
cout << ans << endl;
}