P8218 【深进1.例1】求区间和 (前缀和)
#include <bits/stdc++.h>
using namespace std;
int n, m, a[100010], sum[100010], ans, l, r;
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
sum[i]=sum[i-1]+a[i];
}
scanf("%d", &m);
while(m--){
scanf("%d %d", &l, &r);
ans=sum[r]-sum[l-1];
printf("%d\n", ans);
}
return 0;
}
P6568 [NOI Online #3 提高组] 水壶
#include <bits/stdc++.h>
using namespace std;
const int N=1000010;
int n, k, a[N], sum[N], ans;
int main()
{
scanf("%d %d", &n, &k);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=1; i+k<=n; ++i){
ans=max(ans, sum[i+k]-sum[i-1]);
}
printf("%d", ans);
return 0;
}
P1865 A % B Problem(判断质数+区间查询+前缀和)
#include <bits/stdc++.h>
using namespace std;
int l, r, n, m, sum[1000010];
bool check(int x)
{
if(x<2){
return false;
}
for(int i=2; i<=sqrt(x); ++i){
if(x%i==0){
return false;
}
}
return true;
}
int main()
{
scanf("%d %d", &n, &m);
for(int i=2; i<=m; ++i){
if(check(i)){
sum[i]=sum[i-1]+1;
}
else{
sum[i]=sum[i-1];
}
}
while(n--){
scanf("%d %d", &l, &r);
if(l<1 || l>m || r<1 || r>m){
printf("Crossing the line\n");
}
else if(l>r){
printf("0\n");
}
else{
printf("%d\n", sum[r]-sum[l-1]);
}
}
return 0;
}
P5638 【CSGRound2】光骓者的荣耀
//前缀和
#include <bits/stdc++.h>
using namespace std;
int n, k;
long long a[1000006], sum[1000006], ans, mx;
int main()
{
scanf("%d %d", &n, &k);
for(int i=1; i<n; ++i){
scanf("%lld", &a[i]);
//前缀和, sum[i]表示从1号城市到i+1号城市走高速公路花的时间
//也就是第1条路到第i条路的总时间, 总共有n-1条路
sum[i]=sum[i-1]+a[i];
}
//不能传送
if(k==0){
ans=sum[n-1];
}
else if(k>=n-1){ //能直接传送到终点
ans=0;
}
else{
//能传送连续的k条路, 相当于计算连续k条路的最大和mx,
//然后用总的时间sum[n-1]减去求得的最大和mx就是答案
//第i条路~第i+k-1条路直接传送了
for(int i=1; i+k-1<n; ++i){
// ans=min(ans, sum[n-1]-(sum[i+k-1]-sum[i-1]));
mx=max(mx, sum[i+k-1]-sum[i-1]);
}
ans=sum[n-1]-mx;
}
printf("%lld", ans);
return 0;
}
P3406 海底高铁
#include <bits/stdc++.h>
using namespace std;
int n, m, p[100006], a[100006], b[100006], c[100006];
long long ans, cnt[100006]; //cnt[i]表示第i条铁路走的次数
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=m; ++i){
scanf("%d", &p[i]);
}
for(int i=1; i<n; ++i){
//第i段铁路 可以理解为第i座城市——第i+1座城市之间的铁路
//a[i]表示第i段铁路原始票价
//b[i]表示第i段铁路的折扣票价
//c[i]表示第i段铁路IC卡的工本费
scanf("%d %d %d", &a[i], &b[i], &c[i]);
}
//差分
//cnt[i]表示第i条铁路走的次数
for(int i=1; i<m; ++i){
//p[i]和p[j]表示两座城市的编号
int from=min(p[i], p[i+1]);
int to=max(p[i], p[i+1]);
//从第from座城市出发到第to座城市, 会经过(第form)~(第to-1)条铁路, 所以前面+1, 后面-1
cnt[from]+=1;
cnt[to]-=1;
}
for(int i=1; i<n; ++i){
//前缀和, 求出经过第i条铁路的次数
cnt[i]+=cnt[i-1]; //cnt[0]是0
//总共n-1段铁路, 每一段是买卡还是买票, 取决于:
//买卡的钱c[i]+坐的次数*折扣后的票价b[i] 和 坐的次数*原价a[i]的大小关系
if(c[i]+cnt[i]*b[i]<cnt[i]*a[i]){ //买卡便宜
ans+=c[i]+cnt[i]*b[i];
}
else{ //直接买票便宜
ans+=cnt[i]*a[i];
}
}
printf("%lld", ans);
return 0;
}
P2367 语文成绩
#include <bits/stdc++.h>
using namespace std;
int n, p, x, y, z, mn=510000000, a[5000008], d[5000008]; //学生分数和增加的分数
int main()
{
scanf("%d %d", &n, &p);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
}
for(int i=1; i<=p; ++i){
scanf("%d %d %d", &x, &y, &z);
//区间修改
//标记区间两端
d[x]+=z;
d[y+1]-=z;
}
//前缀和
for(int i=1; i<=n; ++i){
d[i]=d[i-1]+d[i]; //对差分数组求前缀和
a[i]=a[i]+d[i]; //统一修改
mn=min(mn, a[i]); //比较最值
}
printf("%d", mn);
return 0;
}
P1719 最大加权矩形(二维前缀和)
#include <bits/stdc++.h>
using namespace std;
//d[i][j]是二维前缀和数组, 表示从第一行第一列到第i行第j列形成的矩阵数字之和
int n, a[128][128], d[128][128], mx, cur;
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; ++i){
for(int j=1; j<=n; ++j){
scanf("%d", &a[i][j]);
}
}
//计算二维前缀和数组
for(int i=1; i<=n; ++i){ //子矩阵的右下角所在的行
for(int j=1; j<=n; ++j){ //子矩阵的右下角所在的列
d[i][j]=d[i][j-1]+d[i-1][j]-d[i-1][j-1]+a[i][j];
}
}
for(int i=1; i<=n; ++i){ //子矩阵的左上角所在的行
for(int j=1; j<=n; ++j){ //子矩阵的左上角所在的列
//注意k从i开始
for(int k=i; k<=n; ++k){ //子矩阵的右下角所在的行
//注意l从j开始
for(int l=j; l<=n; ++l){ //子矩阵的右下角所在的列
//计算子矩阵的和
cur=d[k][l]-d[k][j-1]-d[i-1][l]+d[i-1][j-1];
mx=max(mx, cur);
}
}
}
}
printf("%d", mx);
return 0;
}
P2004 领地选择(二维前缀和)
//最大子矩阵简化版
#include <bits/stdc++.h>
using namespace std;
int n, m, c, x, y, ans, a[1006][1006], sum[1006][1006];
int main()
{
scanf("%d %d %d", &n, &m, &c);
for(int i=1; i<=n; ++i){
for(int j=1; j<=m; ++j){
scanf("%d", &a[i][j]);
}
}
//维护一个二维前缀和
for(int i=1; i<=n; ++i){
for(int j=1; j<=m; ++j){
sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+a[i][j];
}
}
//设一个擂主
x=1;
y=1;
ans=sum[c][c];
//通过差分求从(i,j)到(i+c-1, j+c-1)的最大矩阵和
for(int i=1; i+c-1<=n; ++i){
for(int j=1; j+c-1<=m; ++j){
int ii=i+c-1; //右下角横坐标
int jj=j+c-1; //右下角纵坐标
if(sum[ii][jj]-sum[i-1][jj]-sum[ii][j-1]+sum[i-1][j-1] > ans){
x=i;
y=j;
ans=sum[ii][jj]-sum[i-1][jj]-sum[ii][j-1]+sum[i-1][j-1];
}
}
}
printf("%d %d", x, y);
return 0;
}
P3397 地毯(二维差分前缀和)
//二维差分
#include <bits/stdc++.h>
using namespace std;
int n, m, sum, leftx, lefty, rightx, righty, ans[1002][1002], d[1002][1002];
int main()
{
scanf("%d %d", &n, &m);
while(m--){
scanf("%d %d %d %d", &leftx, &lefty, &rightx, &righty);
for(int i=leftx; i<=rightx; ++i){
d[i][lefty]+=1;
d[i][righty+1]-=1;
}
}
for(int i=1; i<=n+1; ++i){
for(int j=1; j<=n+1; ++j){
sum+=d[i][j];
ans[i][j]=sum;
}
}
for(int i=1; i<=n; ++i){
for(int j=1; j<=n; ++j){
printf("%d ", ans[i][j]);
}
printf("\n");
}
return 0;
}
P2822 [NOIP2016 提高组] 组合数问题(组合数+二维前缀和)
#include <bits/stdc++.h>
using namespace std;
long long a[2001][2001];
int n, m, t, k, ans[2001][2001];
int main()
{
scanf("%d %d", &t, &k);
for(int i=0; i<=2000; ++i){
a[i][0]=1;
a[i][i]=1;
}
//杨辉三角计算组合数
for(int i=1; i<=2000; ++i){
for(int j=1; j<=i; ++j){
a[i][j]=(a[i-1][j-1]+a[i-1][j])%k;
}
}
for(int i=1; i<=2000; ++i){
for(int j=1; j<=i; ++j){
//前缀和, 上加左、减左上、加自己
ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];
if(a[i][j]==0){
ans[i][j]++;
}
}
//计算ans[i+1][i+1]时, 会用到ans[i][i+1], 由于前面没计算
ans[i][i+1]=ans[i][i];
}
while(t--){
scanf("%d %d", &n, &m);
if(m>n){
printf("%d\n", ans[n][n]);
}
else{
printf("%d\n", ans[n][m]);
}
}
return 0;
}
P2280 [HNOI2003]激光炸弹
处理边界的一种方法
#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, v, asd[5010][5010], cur, ans;
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d %d %d", &x, &y, &v);
asd[x+1][y+1]=v;
}
for(int i=1; i<=5001; ++i){
for(int j=1; j<=5001; ++j){
asd[i][j]=asd[i][j-1]+asd[i-1][j]-asd[i-1][j-1]+asd[i][j];
}
}
for(int i=1; i+m-1<=5001; ++i){
for(int j=1; j+m-1<=5001; ++j){
int xx=i+m-1;
int yy=j+m-1;
cur=asd[xx][yy]-asd[xx][j-1]-asd[i-1][yy]+asd[i-1][j-1];
ans=max(ans, cur);
}
}
printf("%d", ans);
return 0;
}
处理边界的另一种方法
#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, v, cur, ans, asd[5010][5010];
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d %d %d", &x, &y, &v);
asd[x+1][y+1]=v;
}
for(int i=1; i<=5001; ++i){
for(int j=1; j<=5001; ++j){
asd[i][j]=asd[i][j-1]+asd[i-1][j]-asd[i-1][j-1]+asd[i][j];
}
}
for(int i=m; i<=5001; ++i){
for(int j=m; j<=5001; ++j){
int starti=i-m+1;
int startj=j-m+1;
cur=asd[i][j]-asd[i][startj-1]-asd[starti-1][j]+asd[starti-1][startj-1];
ans=max(ans, cur);
}
}
printf("%d", ans);
return 0;
}
P3131 [USACO16JAN]Subsequences Summing to Sevens S
(前缀和思维题)
#include <bits/stdc++.h>
using namespace std;
const int N=50010;
int n, a[N], sum[N], l[7], r[7], ans;
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
sum[i]=(sum[i-1]+a[i])%7;
if(!l[sum[i]]){ //第一次出现这个余数,
l[sum[i]]=i; //记录第一次出现的下标
}
r[sum[i]]=i; //记录最后一次出现的下标
}
for(int i=1; i<=6; ++i){
ans=max(ans, r[i]-l[i]); //如果没答案, 则ans为0
}
ans=max(ans, r[0]);
printf("%d", ans);
return 0;
}