一维前缀和:类似我们的前N项和
原数组: a1 , a2 , a3 , a4 , a5 , a6 , a7
前缀和: Si=a1+a2+a3+…….+ai;
题目:对于每个询问,输出原序列中从第l个数到第r个数的和
第一行包含2个整数:n和m
第二行包含n个整数,表示整块数列
接下来m行,每行都包含2个整数l和r,表示一个询问区间范围
#include<iostream>
using namespace std;
const int N = 100010;
int n, m,l,r;
int a[N], s[N] = { 0 };
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]); // 前缀和初始化
for (int i = 1; i <= n; i++)
s[i] = s[i - 1] + a[i]; // 区间和计算
while (m--) {
scanf("%d %d", &l, &r);
cout << s[r] - s[l - 1] << endl;// ai =si+1 -si;
}
}
二维前缀和
一维可以表示长度,二维表示面积,a[i]表示0——i元素和,a[i][j] dp[i][j]代表长i宽j的矩阵的元素和
A[i][i]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1]
#include<iostream>
using namespace std;
const int N = 1010;
int n, m, q,i,j;
int a[N][N], s[N][N];
int main() {
scanf_s("%d%d%d", &n, &m, &q);
for ( i = 1; i <= n; i++)
for ( j = 1; j <= m; j++)
scanf_s("%d", &a[i][j]);
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++)
s[i][j] = s[i - 1][j ] + s[i][j - 1] - s[i - 1][j - 1]+a[i][j];
while (q--) {
int x1, y1, x2, y2;
scanf_s("%d%d%d%d", &x1, &y1, &x2,&y2);
printf("%d\n", s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);
}
}
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int arr[1010][1010];
int r,l,n,m;
int main(){
int t;
cin>>t;
while(t--){
memset(arr,0,sizeof(arr));
int Max1=0;
cin>>n>>m>>r>>l;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>arr[i][j];
arr[i][j]+=arr[i-1][j]+arr[i][j-1]-arr[i-1][j-1];
if(i>=r&&j>=l){
Max1=max(Max1,arr[i][j]-arr[i-r][j]-arr[i][j-l]+arr[i+r][j+l]);
}
}
cout<<Max1<<endl;
}
return 0;
}
一维差分:
如果有一数列 a[1],a[2],.…a[n]
且令 b[i]=a[i]-a[i-1],b[1]=a[1]
那么就有
a[i]=b[1]+b[2]+.…+b[i]
此时b数组称作a数组的差分数组
换句话来说a数组就是b数组的前缀和数组 例:
原始数组a:9 3 6 2 6 8
差分数组b:9 -6 3 -4 4 2
可以看到a数组是b的前缀和,所以可以说差分与前缀和是互逆操作
来先看一道裸题,有n个数。
m个操作,每一次操作,将x~y区间的所有数增加z;
最后有q个询问,每一次询问求出x~y的区间和。
题目地址https://www.acwing.com/problem/content/799/
#include<iostream>
using namespace std;
const int N = 100010;
int a[N], b[N];
int n, m,i,l,r,c;
// 核心算法
void insert(int l, int r, int c) {
b[l] += c;
b[r+1] -= c;
}
int main(){
scanf("%d%d", &n, &m);
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (i = 1; i <= n; i++)
insert(i, i, a[i]); // 得到原来b序列
while (m--) {
scanf("%d%d%d", &l, &r, &c);
insert(l, r, c);
}
for (i = 1; i <= n; i++)
b[i] += b[i - 1];
for (i = 1; i <= n; i++)
printf("%d ", b[i]);
}
二维差分:
一个n*m的整数矩阵 再输入q个操作包括五个整数x1,y1,x2,y2,c;
其中(x1,y1)和(x2,y2)表示一个子矩阵的左上角坐标和右下角坐标
每个操作要把选中的子矩阵中的每个元素的值都加上c
推荐博客:https://blog.csdn.net/justidle/article/details/104506724
#include<iostream>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], b[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
b[x1][y1] += c;
b[x1][y2+1] -=c;
b[x2+1][y1] -=c;
b[x2+1][y2+1] +=c;
https://www.cnblogs.com/LMCC1108/p/10753451.html
}
int main()
{
cin.tie(0);
cin >> n >> m >>q;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
{
cin >> a[i][j];
insert(i,j,i,j,a[i][j]);
}
while(q--)
{
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
insert(x1,y1,x2,y2,c);
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
b[i][j] += b[i-1][j] + b[i][j-1] - b[i-1][j-1];
cout << b[i][j] <<" ";
}
cout << endl;
}
}
Hdu 6514
双指针:
解题思路:双指针技巧中的左右指针,确定两个指针,l与r,子串的左边界一旦确定,让 l 固定,让r先后移动,直到k个字符出现,r前部分的字符对子字符没有影响,所以从 l 开始的子字符串 为 lenght-r+1,就如此遍历左端点就可以了,将结果累加起来
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
using namespace std;
#define ll long long
const int maxn=1e6+7;
const ll INF=1e9+7;
char s[maxn];
ll vis[maxn];
ll n;
int main(){
ll t;
scanf("%lld",&t);
while(t--){
memset(vis,0,sizeof(vis));
ll ans=0;
scanf("%s",&s);
scanf("%lld",&n);
ll l=0;
ll r=0;
ll k=0;
ll len=strlen(s);
while(l<len){
while(r<len&&k<n){
if(vis[s[r]-'0']==0)
k++;
vis[s[r]-'0']++;
r++;
}
if(k<n) break;
ans+=len-r+1;
vis[s[l]-'0']--;
if(vis[s[l]-'0']==0)
k--;
l++;
}
printf("%lld\n",ans);
}
return 0;
}