前缀和相关题:
「USACO16JAN」子共七 Subsequences Summing to Sevens
定理:若两个数相减 mod k == 0,那么这两个数mod k 的余数相同。(看别人的题解得到的)
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4;
int n;
int a[N + 1];//前n项和mod 7
int bgin[7],tail[7]; //bgin存第1个mod为i的数据编号,tail存最后一个mod为i的数据编号
int main()
{
// 若两个数相减mod 7==0,那么这俩个数mod 7的余数一定相同
ios::sync_with_stdio(false);
int res = 0;//存结果
cin >> n ;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
a[i] += a[i-1];
a[i] %= 7;
}
for(int i = 0; i <= n; i ++) //从前往后搜,且这里必须是i=0开始,不能从i=1开始,考虑数据1 3 4即明白
tail[a[i]] = i;
for(int i = n; i >= 0; i --) //从后往前搜, 并且这里必须要包含0也即i>=0
bgin[a[i]] = i;
for(int i = 0; i < 7; i ++)
res = max(res, tail[i] - bgin[i]);
cout << res << endl;
return 0;
}
公式推到:(假设数据有序)
第i个位置的牛与前i-1个牛聊天音量的总和:
x
i
−
x
i
−
1
+
x
i
−
x
i
−
2
+
⋯
+
x
i
−
x
2
+
x
i
−
x
1
=
(
i
−
1
)
⋅
∑
k
=
1
i
−
1
x
k
x_i - x_{i-1} + x_i - x_{i-2} +\cdots+ x_i -x_2+x_i - x_1 = (i-1)\cdot\sum\limits_{k=1}^{i-1}x_k
xi−xi−1+xi−xi−2+⋯+xi−x2+xi−x1=(i−1)⋅k=1∑i−1xk
下式res结果要乘2 是因为只计算了
(
x
i
>
x
j
)
时
x
i
−
x
j
(x_i>x_j)时x_i-x_j
(xi>xj)时xi−xj并没有计算
x
j
−
x
i
x_j-x_i
xj−xi,且
∣
x
i
−
x
j
∣
=
∣
x
j
−
x
i
∣
\lvert x_i-x_j\rvert=\lvert x_j-x_i\rvert
∣xi−xj∣=∣xj−xi∣。
r e s = 2 ⋅ ∑ i = 1 n ( i − 1 ) ⋅ ∑ k = 1 i − 1 x k res =2\cdot \sum\limits_{i=1}^{n}(i-1)\cdot\sum\limits_{k=1}^{i-1}x_k res=2⋅i=1∑n(i−1)⋅k=1∑i−1xk
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 1;
typedef long long ll;
ll n;
ll a[N]; //存放数字
ll sum[N]; //前n项和
int main()
{
ios::sync_with_stdio(false);
cin >> n;
ll res = 0;//结果
for(int i = 1; i <= n; i ++)
cin >> a[i];
//排序
sort(a+1,a+n+1);
//求前n项和
for(int i = 1; i <= n; i ++)
sum[i] = sum[i-1] + a[i];
// 求最终结果
for(int i = 1; i <= n; i ++)
res += (i - 1)*a[i] - sum[i-1];//此公式需要推到
cout << res * 2 << endl;
}
二维前缀和:
#include<bits/stdc++.h>
using namespace std;
const int N = 100 + 3;
int n, m;
int matrix[N][N];
int sum[N][N]; //前缀和
int main()
{
ios::sync_with_stdio(false);
int res = 0;
cin >> n >> m;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
{
cin >> matrix[i][j];
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + matrix[i][j];
}
for(int len = 1; len <= min(n, m); len ++)//边长
for(int i = len; i <= n; i ++)
for(int j = len; j <= m; j ++)
{
if((sum[i][j] - sum[i - len][j] - sum[i][j - len] + sum[i - len][j - len]) == (len * len))
res = max(res, len);
}
cout << res << endl;
return 0;
}
以下都是AC代码,为了防止MLE进行了空间优化,也即sum即存储原始数据,也存储前缀和。
#include<bits/stdc++.h>
using namespace std;
const int N = 5e3 + 3;
int n, m;
int sum[N][N];//前缀和
int main()
{
ios::sync_with_stdio(false);
int res = 0, mx = 0, my = 0;
cin >> n >> m;
for(int i = 0; i < n; i ++)
{
int x, y, v;
cin >> x >> y >> v;
sum[x + 1][y + 1] = v;
mx = max({mx, x + 1, m});
my = max({my, y + 1, m});//必须把m算进去,考虑此类情况:n=1 m= 2 ,第二行输入 0,0 ,1
}
for(int i = 1; i <= mx; i ++)
for(int j = 1; j <= my; j ++)
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + sum[i][j];
for (int i = m; i <= mx; i ++)
for(int j = m; j <= my; j ++)
res = max(res, sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m]);
cout << res << endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 5000 + 11;
int n, m;
int sum[N][N];//前缀和
int main()
{
ios::sync_with_stdio(false);
int res = 0, mx = 0, my = 0;
cin >> n >> m;
for(int i = 0; i < n; i ++)
{
int x, y, v;
cin >> x >> y >> v;
sum[x + 1][y + 1] = v;//这个是让坐标从1开始,而不是从0开始
mx = max(mx, x + 1);
my = max(my, y + 1);
}
for(int i = 1; i <= min(mx + m, 5001); i ++) //这里i不能小于等于N,因为你数组就开N个, 当然如果这里为了省事,可直接i<=5001 下面一样
for(int j = 1; j <= min(my + m, 5001); j ++)
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + sum[i][j];//这里其实进行了空间的优化
for (int i = m; i <= min(mx + m, 5001); i ++)
for(int j = m; j <= min(my + m, 5001); j ++)
res = max(res, sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m]);
cout << res << endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 600 + 3;
int n, l, r, t;
int sum[N][N];
int main()
{
ios::sync_with_stdio(false);
int res = 0;
cin >> n >> l >> r >> t;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
{
cin >> sum[i][j];
sum[i][j] = sum[i-1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + sum[i][j];
}
// 中心点为参考
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
{
int loc_l = max(1, j - r);
int loc_r = min(n, j + r);//不能直接把变量设为r,t,会与前面的变量名重复。
int loc_t = max(1, i - r);
int loc_b = min(n, i + r);
double counts = (loc_r - loc_l + 1) * (loc_b - loc_t + 1); //个数
if( ((sum[loc_b][loc_r] - sum[loc_b][loc_l - 1] - sum[loc_t - 1][loc_r] + sum[loc_t - 1][loc_l - 1]) / counts) <= t)
res += 1;
}
cout << res << endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + 1;
int m;
struct Node
{
int y,result;
bool operator < (const struct Node n)//根据y排序
{
if (y < n.y)
return true;
return false;
}
}node[M];
int sum[M];
int main()
{
ios::sync_with_stdio(false);
int res = 0, maxTotal = 0;//res:最大阈值,maxTotal: 预测正确次数最大
cin >> m;
for(int i = 1; i <= m; i ++)
cin >> node[i].y >> node[i].result;
sort(node + 1, node + m + 1);
for(int i = 1; i <= m; i ++)
sum[i] = sum[i-1] + node[i].result;
for(int i = 1; i <= m; i ++)
{
if(node[i].y == node[i-1].y) //这一步是必须的,如果y连续出现相同的数,只需计算第一个,后面的不要再计算 ,因为下面的sum[i-1]会出错
continue;
int total = (i - 1 - sum[i-1]) + (sum[m] - sum[i - 1]);//计算预测正确次数, +前部分是 小于y的预测正确的次数,+ 后半部分是 大于y预测正确的次数
//找最大的maxtotal和阈值
if(maxTotal <= total)
{
maxTotal = total;
res = node[i].y;
}
}
cout << res << endl;
return 0;
}
差分相关题:
a
i
+
1
=
b
i
+
1
−
b
i
a_{i+1} = b_{i+1} - b_i
ai+1=bi+1−bi其中
a
a
a是差分矩阵,
b
b
b是原矩阵,对差分矩阵求前缀和即是原矩阵。如果对原矩阵
[
l
,
r
]
[l,r]
[l,r]增加k,那么可以对差分矩阵
a
[
l
]
+
=
k
,
a
[
r
+
1
]
−
=
k
a[l] += k, a[r + 1] -= k
a[l]+=k,a[r+1]−=k。可以减少时间复杂度。
∑
i
=
1
n
a
i
=
∑
i
=
1
n
∑
j
=
1
i
b
j
=
∑
i
=
1
n
(
n
−
i
+
1
)
b
i
=
(
n
+
1
)
∑
i
=
1
n
b
i
−
∑
i
=
1
n
i
⋅
b
i
\sum\limits_{i=1}^n a_i = \sum\limits_{i=1}^n \sum\limits_{j=1}^i b_j = \sum\limits_{i=1}^n (n - i + 1) b_i = (n + 1)\sum\limits_{i =1}^n b_i-\sum\limits_{i = 1}^n i\cdot b_i
i=1∑nai=i=1∑nj=1∑ibj=i=1∑n(n−i+1)bi=(n+1)i=1∑nbi−i=1∑ni⋅bi
有关
∑
i
=
1
n
∑
j
=
1
i
b
j
=
∑
i
=
1
n
(
n
−
i
+
1
)
b
i
\sum\limits_{i=1}^ n \sum\limits_{j=1}^ i b_j = \sum\limits_{i=1}^n (n - i + 1) b_i
i=1∑nj=1∑ibj=i=1∑n(n−i+1)bi 这一步的产生,考虑有n个
b
1
b_1
b1,n-1个
b
2
b_2
b2 ,以此类推有
n
−
i
+
1
n-i+1
n−i+1个
b
i
b_i
bi故得出结果。
q + k ≤ t i ≤ q + k + c i − 1 q + k \le t_i \le q +k+c_i -1 q+k≤ti≤q+k+ci−1 则 t i + 1 − c i − k ≤ q ≤ t i − k t_i + 1- c_i - k \leq q \leq t_i - k ti+1−ci−k≤q≤ti−k,也即做核酸的时间点要在 [ t i + 1 − c i − k , t i − k ] [t_i + 1- c_i - k , t_i - k] [ti+1−ci−k,ti−k]内,对每个出行计划其范围内的时间点+1,时间点为整数,最终如果求q时刻满足出行的计划,直接根据下标q即可得。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 3;
int n, m, k;
int diff[N]; // 差分矩阵 ,差分矩阵的原矩阵表示在每个时间点,如果这个时间做核酸可以出行,则这里的值+1
int q[N];
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m >> k;
for(int i = 0; i < n; i ++)
{
int ti, ci;
cin >> ti >> ci;
diff[max(1,ti + 1 - ci - k)] ++;//这里要和 1进行比较,如果不加会出现错误。
diff[max(1,ti - k + 1)] --;
}
for(int i = 1; i < N; i ++)
diff[i] = diff[i - 1] + diff[i];
for(int i = 0; i < m; i ++)
cin >> q[i];
for(int i = 0; i < m; i ++)
cout << diff[q[i]] << endl;
return 0;
}
P3397 地毯此题是很经典的二维差分问题。有关二维差分参见此博客
#include<bits/stdc++.h>
using namespace std;
const int N = 1000 + 3;
int n, m;
int diff[N][N];
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 0; i < m; i ++)
{
int lx, ly, rx, ry;
cin >> lx >> ly >> rx >> ry;
diff[lx][ly] ++;
diff[lx][ry + 1] --;
diff[rx + 1][ly] --;
diff[rx + 1][ry + 1] ++;
}
//求前缀和
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
{
diff[i][j] = diff[i-1][j] + diff[i][j - 1] - diff[i - 1][j - 1] + diff[i][j];//这里是根据差分求原矩阵
}
//输出
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
cout << diff[i][j] << " ";
}
cout << endl;
}
return 0;
}
P4552 [Poetize6] IncDec Sequence
题解实在是不知道该怎么说,洛谷有相关题解,第一个写的很好。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100000 + 3;
LL n;
LL diff[N];//a是原矩阵,diff是差分矩阵 ,必须是LL,int会报错,2^31,需要开LL
int main()
{
ios::sync_with_stdio(false);
LL count_operator = 0, count_result = 0;
cin >> n;
LL last = 0;
for(int i = 1; i <= n; i ++)
{
LL t;
cin >> t;
diff[i] = t - last;
last = t;
}
LL sum_p = 0,sum_n = 0;
for(int i = 2; i <= n; i ++)
{
if(diff[i] < 0)
sum_n += diff[i];
else if(diff[i] > 0)
sum_p += diff[i];
}
count_operator = abs(sum_p + sum_n) + min(sum_p, abs(sum_n));
count_result = abs(sum_p + sum_n) + 1;
cout << count_operator << endl;
cout << count_result << endl;
return 0;
}
树状数组
/*
单点修改,区间查询 2022.9.9
计算机中,负数用补码表示 ,所以lowbit = x & -x
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 3;
ll t[N]; //存储的原数组的树状数组。
struct Node
{
ll id, l, r;
}node[N];
ll n, q;
//单点修改,对下标为i的原数组增加x
void add(ll x, ll k)
{
for(;x <= n; x += x & -x)
t[x] += k;
}
// 求下标x的原数组前缀和
ll prefix(ll x)
{
ll ans = 0;
for(;x >= 1; x -= x & -x)
ans += t[x];
return ans;
}
// 求区间原数组[l,r]的区间和
ll quary(ll l, ll r)
{
return prefix(r) - prefix(l - 1);
}
int main()
{
cin >> n >> q;
for(ll i = 1; i <= n; i ++)
{
ll temp;
cin >> temp;
add(i, temp);
}
for(ll i = 0; i < q; i ++)
cin >> node[i].id >> node[i].l >> node[i].r;
for(ll i = 0; i < q; i ++)
{
if(node[i].id == 1)
add(node[i].l, node[i].r);
else if(node[i].id == 2)
cout << quary(node[i].l, node[i].r) << endl;
}
return 0;
}
写成模板
/*
单点修改,区间查询 2022.9.9
计算机中,负数用补码表示 ,所以lowbit = x & -x
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//写成模板
const ll N = 1e6 + 3;
struct BIT
{
ll t[N], n; //t[n]存储的原数组的树状数组,n是元素组的元素个数。
//单点修改,对下标为i的原数组增加x
inline void add(ll x, ll k)
{
for(;x <= n; x += x & -x)
t[x] += k;
}
// 求下标x的原数组前缀和
inline ll prefix(ll x)
{
ll ans = 0;
for(;x >= 1; x -= x & -x)
ans += t[x];
return ans;
}
// 求区间原数组[l,r]的区间和 ,区间查询
inline ll quary(ll l, ll r)
{
return prefix(r) - prefix(l - 1);
}
};
struct BIT bit;
struct Node
{
ll id, l, r;
}node[N];
ll q;
int main()
{
ios::sync_with_stdio(false);
cin >> bit.n >> q;
for(ll i = 1; i <= bit.n; i ++)
{
ll temp;
cin >> temp;
bit.add(i, temp);
}
for(ll i = 0; i < q; i ++)
cin >> node[i].id >> node[i].l >> node[i].r;
for(ll i = 0; i < q; i ++)
{
if(node[i].id == 1)
bit.add(node[i].l, node[i].r);
else if(node[i].id == 2)
cout << bit.quary(node[i].l, node[i].r) << endl;
}
return 0;
}
/*
time: 2022.9.9
区间修改,单点查询
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 3;
struct BIT
{
ll n, t[N]; //t[N]存储的是原数组的差分数组的树状数组
//单点修改 (这里的单点是指对差分数组中的一个元素修改,而非原数组)
inline void add(ll x,ll k)
{
for(; x<= n; x += x & -x)
t[x] += k;
}
//区间修改,对原数组的[l,k]+k(指的是对原数组进行区间修改)
inline void inter_add(ll l, ll r, ll k)
{
add(l, k);
add(r + 1, -k);
}
//单点查询,(这里的单点查询,是对原数组求其某下标的值),其实是求差分数组的前缀和
inline ll ask(ll x)
{
ll ans = 0;
for(;x >= 1; x -= x & -x)
ans += t[x];
return ans;
}
};
struct BIT bit;
ll q;
struct Qu
{
ll id,l,r,x;
}Q[N];
int main()
{
cin >> bit.n >> q;
ll last = 0;
for(ll i = 1; i <= bit.n; i ++)
{
ll temp;
cin >> temp;
bit.add(i, temp - last); // 在进行构造过程中,不能 bit.add(i, temp);,而应该用差分数组
last = temp;
}
for(ll i = 1; i <= q; i ++)
{
cin >> Q[i].id;
if(Q[i].id == 1)
cin >> Q[i].l >> Q[i].r >> Q[i].x;
else if(Q[i].id == 2)
cin >> Q[i].l;
}
for(ll i = 1; i <= q; i ++)
{
if(Q[i].id == 1)
bit.inter_add(Q[i].l, Q[i].r, Q[i].x);
else if(Q[i].id == 2)
{
ll res = bit.ask(Q[i].l);
cout << res << endl;
}
}
return 0;
}
/*
time: 2022.9.9
区间修改,区间查询;
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 3;
struct BIT
{
ll t1[N], t2[N], n;//t1[N]是差分数组的树状数组,t2[N]是i*diff[i]的树状数组
//t1单点修改(这里是对差分数组单点修改)
inline void add1(ll x, ll k)
{
for(; x <= n; x += x & -x)
t1[x] += k;
}
//t2 单点修改(这里是对i*diffi单点修改)
inline void add2(ll x, ll k)
{
for(; x <= n; x += x & -x)
t2[x] += k;
}
//求 diff从1到x的前缀和
inline ll prefix1(ll x)
{
ll ans = 0;
for(; x >= 1; x -= x & -x)
ans += t1[x];
return ans;
}
//求i* diff的从1到x前缀和
inline ll prefix2(ll x)
{
ll ans = 0;
for(; x >= 1; x -= x & -x)
ans += t2[x];
return ans;
}
//区间查询,求原数组[l,r]的区间和
inline ll inter_sum(ll l, ll r)
{
return ((r+1)*prefix1(r) - prefix2(r)) - (l*prefix1(l - 1) - prefix2(l - 1));
}
//区间修改,对元素组[l,r]+k
inline void inter_add(ll l, ll r, ll k)
{
add1(l, k);
add1(r + 1, -k);
//同时需要对 i*diff这个数组进行修改
add2(l, l * k);
add2(r + 1, - (r + 1) * k);
}
};
struct BIT bit;
ll q;
struct Node
{
ll id, l, r, x;
}Q[N];
int main()
{
ios::sync_with_stdio(false);
cin >> bit.n >> q;
ll last = 0;
for(ll i = 1;i <= bit.n; i ++)
{
ll temp;
cin >> temp;
bit.add1(i, temp - last);
bit.add2(i, i * (temp - last));
last = temp;
}
for(ll i = 1; i <= q; i ++)
{
cin >> Q[i].id;
if(Q[i].id == 1)
cin >> Q[i].l >> Q[i].r >> Q[i].x;
else if(Q[i].id == 2)
cin >> Q[i].l >> Q[i].r;
}
for(ll i = 1; i <= q; i ++)
{
if(Q[i].id == 1)
bit.inter_add(Q[i].l, Q[i].r, Q[i].x);
else if(Q[i].id == 2)
cout << bit.inter_sum(Q[i].l, Q[i].r) << endl;
}
return 0;
}