Problem A
思路分析
简单的前缀和板子,不多说。
AC代码
#includeusing namespace std; using ll =unsigned long long; ll n, m; ll a[200000]; ll pre[200000], sum[200000]; int main() {cin >> n; for (ll i = 1; i <= n; i++) {cin >> a[i]; pre[i] = a[i] + pre[i - 1]; } cin >> m; for (ll i = 1; i <= m; i++) {ll l = 0, r = 0; cin >> l >> r; sum[i] = pre[r] - pre[l - 1]; } for (ll i = 1; i <= m; i++) {cout << sum[i] << endl; } return 0; }
Problem B
其实是二维前缀和的板子,这里给出几份代码,看看不同的思路(大多都是细节上处理的方法不同)。
AC代码
CODE1
#include#include using namespace std; using ll = long long; int n, ans = -99999999; int squ[130][130]; int sum[130][130]; int lin[130][130]; int main() {cin >> n; for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {cin >> squ[i][j]; //lin[i][j]表示的是第i行前j个(其实也就是第j列)数字的和 lin[i][j] = lin[i][j - 1] + squ[i][j]; //sum[i][j]表示的是以第1行第1个数字为左上角,以第i行第j个数字为右下角的矩形的面积。 sum[i][j] = sum[i - 1][j] + lin[i][j]; } } //分别枚举左上角的坐标(x1,y1)和右下角(x2,y2)的坐标。 for (int x1 = 1; x1 <= n; x1++) {for (int y1 = 1; y1 <= n; y1++) {for (int x2 = 1; x2 <= n; x2++) {for (int y2 = 1; y2 <= n; y2++) {if (x2 < x1||y2 continue; } ans = max(ans, sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] +sum[x1-1][y1-1]); } } } } cout << ans; return 0; }
可能有人对 s u m [ x 2 ] [ y 2 ] − s u m [ x 2 ] [ y 1 − 1 ] − s u m [ x 1 − 1 ] [ y 2 ] + s u m [ x 1 − 1 ] [ y 1 − 1 ] sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] +sum[x1-1][y1-1] sum[x2][y2]−sum[x2][y1−1]−sum[x1−1][y2]+sum[x1−1][y1−1]为什么是以 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)为左上角,以 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)为右下角的矩形的面积有点疑惑。画个图解释一下:
这下应该能看懂了吧qwq。
CODE2
#includeusing namespace std; using ll=long long; int a[125][125],mat[125][125]; int query(int i,int j,int k,int m){ //还是跟上面的操作一样 return mat[k][m]-mat[k][j-1]-mat[i-1][m]+mat[i-1][j-1]; } int main(){ int n;cin>>n; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ cin>>a[i][j]; //预处理出以(i,j)为右下角的矩形的面积,画画图就懂了 mat[i][j]=mat[i-1][j]+mat[i][j-1]-mat[i-1][j-1]+a[i][j]; } } int ans=-1e9; //枚举左上角(i,j),右下角(k,m) for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ //注意k>=i,m>=j for(int k=i;k<=n;k++){ for(int m=j;m<=n;m++){ ans=max(ans,query(i,j,k,m)); } } } } cout< CODE3
实际上是在枚举时优化了一下,降低时间复杂度。
#includeusing namespace std; using ll=long long; int a[125][125],mat[125][125]; int query(int i,int j,int k,int m){ return mat[k][m]-mat[k][j-1]-mat[i-1][m]+mat[i-1][j-1]; } int main(){ int n;cin>>n; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ cin>>a[i][j]; mat[i][j]=mat[i-1][j]+mat[i][j-1]-mat[i-1][j-1]+a[i][j]; } } int ans=-1e9,sum; //这里改成了枚举矩形的上下边i与j for(int i=1;i<=n;i++){ for(int j=i;j<=n;j++){ sum=0; //k枚举的是第几列,从左往右扫描每一列的面积,并加到sum中 for(int k=1;k<=n;k++){ sum+=query(i,k,j,k); //如果sum>ans,说明在以i行为上界,j行为下界的,并且右界限为k的构成的连续矩形面积(即sum)更大,更新ans if(sum>ans)ans=sum; //如果sum<0,那么只能另开一段,置sum为0 if(sum<0)sum=0; } } } cout< 或者可以理解成是矩阵压缩,可以看看题解的第一篇,本质其实是一样的。
P1719 最大加权矩形
Problem C
思路分析
差分的板子题,但是我经常忘记怎么操作了,贴上证明。
这里贴的是GoldenFishX大佬的博客,可以看看(大佬如果觉得侵权,联系我删除即可qwq)。
AC代码
#includeusing namespace std; using ll = long long; ll p, n, a[5000500], d[5000500]; ll x, y, z, ans=9999999999; int main() {cin >> n >> p; for (ll i = 1; i <= n; i++) {cin >> a[i]; //求出差分数组 d[i] = a[i] - a[i - 1]; } for (ll i = 1; i <= p; i++) {cin >> x >> y >> z; //上面讲得很清楚了 d[x] += z; d[y + 1] -= z; } for (ll i = 1; i <= n; i++) { //差分的性质:a[i]=d[i]+d[i-1]+d[i-2]+······+d[1] a[i] = a[i - 1] + d[i]; ans = min(ans, a[i]); } cout << ans; return 0; } Problem D
这题其实可以暴力模拟水过去,但实际上正解是二维差分。(补学一下qwq)。
设数组 a a a的差分数组为 b b b,则:
b [ [ i ] [ j ] = a [ i ] [ j ] − a [ i − 1 ] [ j ] − a [ i ] [ j − 1 ] + a [ i − 1 ] [ j − 1 ] b[[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1] b[[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1].
(偷懒了,直接贴书上的内容qwq)
可以看到(a),(b)图中,右下角的矩形中的各点都+1了,可以试着结合一维差分的证明理解一下wsm。©,(d)图其实就是相同的操作罢了。
AC代码
#includeusing namespace std; using ll=long long; const int MAXN=1e3+10; int mat[MAXN][MAXN]; int main(){ int n,m;cin>>n>>m; for(int i=1;i<=m;i++){ int x1,y1,x2,y2; cin>>x1>>y1>>x2>>y2; //上面提到的操作 mat[x1][y1]++; mat[x2+1][y1]--; mat[x1][y2+1]--; mat[x2+1][y2+1]++; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ //求前缀和,处理出每个点的值 mat[i][j]+=mat[i-1][j]+mat[i][j-1]-mat[i-1][j-1]; cout< Problem E
实际上这是一道离散化的题目,但是我当初做的时候还不会离散化qwq,看了题解区大佬的绝妙思路:
大佬的题解
可以发现,覆盖的范围是一样的,那么我们可以这样操作:
1、将起点和终点排个序。
2、将他们按照从小到大的顺序一一匹配,计算长度。
3、如果有重复的覆盖范围,减去即可。(也就是当前的终点坐标比下一个的起点坐标大时)
AC代码
#include#include using namespace std; using ll = long long; int n; ll a[20200], b[20200], l; int main() {cin >> n; for (int i = 1; i <= n; i++) {cin >> a[i] >> b[i]; } sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + n); for (int i = 1; i <= n; i++) {l += b[i] - a[i]; //注意i if (b[i] > a[i + 1]) {l -= b[i] - a[i + 1]; } } } cout << l; return 0; } Problem F
二维前缀和的练习题,不解释了qwq。
AC代码
#includeusing namespace std; using ll = long long; ll n, m, c; ll map[1010][1010], sum[1010][1010], maxn = -999999999, nx,ny; int main() {cin >> n >> m >> c; for (ll i = 1; i <= n; i++) {for (ll j = 1; j <= m; j++) {cin >> map[i][j]; //求前缀和 sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + map[i][j]; } } for (int i = c; i <= n; i++) {for (int j = c; j <= m; j++) { //枚举以(i,j)为右下角、边长为c的正方形的面积,注意更新坐标 if (sum[i][j] - sum[i - c][j] - sum[i][j - c] + sum[i - c][j - c] > maxn) {maxn = sum[i][j] - sum[i - c][j] - sum[i][j - c] + sum[i - c][j - c]; nx = i - c + 1; ny = j - c + 1; } } } cout << nx << " " << ny; return 0; }