P8218 【深进1.例1】求区间和
题目描述
给定由 n 个正整数组成的序列 a1,a2,⋯,an 和 m 个区间 [li,ri],分别求这 m 个区间的区间和。
输入格式
第一行包含一个正整数 n,表示序列的长度。
第二行包含 n 个正整数 a1,a2,⋯,an。
第三行包含一个正整数 m,表示区间的数量。
接下来 m 行,每行包含两个正整数 li,ri,满足 1≤li≤ri≤n。
输出格式
共 m 行,其中第 i 行包含一个正整数,表示第 i 组答案的询问。
输入输出样例
输入 #1复制
4
4 3 2 1
2
1 4
2 3
输出 #1复制
10
5
说明/提示
样例解释
第 1 到第 4 个数加起来和为 10。第 2 个数到第 3 个数加起来和为 5。
数据范围
对于 50% 的数据:n,m≤1000;
对于 100% 的数据:1≤n,m≤105,1≤ai≤104。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
int a[111100];
int sum[111001];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]+=sum[i-1]+a[i];
}
int m;
cin>>m;
for(int i=1;i<=m;i++){
int l,r;
cin>>l>>r;
cout<<sum[r]-sum[l-1]<<endl;
}
return 0;
}
P1719 最大加权矩形
题目描述
为了更好地备战 NOIP 2013,电脑组的几个女孩子 LYQ,ZSC,ZHQ 认为:我们不光需要机房,我们还需要运动。于是她们决定找校长申请一块电脑组的课余运动场地。听说她们都是电脑组的高手,校长没有马上答应他们,而是先给她们出了一道数学题,并告诉她们,她们能获得的运动场地的面积就是她们能找到的这个最大的数字。
校长给她们一个大小为 n×n 的矩阵,矩阵中的每一个元素都有一个整数权值,要她们求出该矩阵中的最大加权矩形(即从中找一大小不限的矩形,使其中包含的所有元素的权值和最大)中所有元素的权值和,且矩阵中每个元素的权值均在区间 [−127,127] 内。
几个女孩子有点犯难了,于是就找到了电脑组精打细算的 HZH,TZY 小朋友帮忙计算,但是遗憾的是,他们的答案都不一样。涉及土地的事情我们可不能含糊,你能帮忙计算出校长所给的矩形中加权和最大的矩形吗?
输入格式
第一行包含一个正整数 n。
接下来 n 行每行包含 n 个整数,表示给定的矩阵。
输出格式
输出一行一个整数,表示该矩阵的最大加权矩形中所有元素的权值和。
输入输出样例
输入 #1复制
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
输出 #1复制
15
说明/提示
样例解释
该矩阵中的最大加权矩形为
9 2
-4 1
-1 8
它们的和为 15。
数据范围
对于 100% 的数据,1≤n≤120
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,maxn;
const int N=122;
int a[N][N],sum[N][N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
a[i][j]+=a[i][j-1];
sum[i][j]+=a[i][j]+sum[i-1][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int a=1;a<=i;a++){
for(int b=1;b<=j;b++){
maxn=max(maxn,sum[i][j]+sum[a-1][b-1]-sum[i][b-1]-sum[a-1][j]);
}
}
}
}
cout<<maxn;
return 0;
}
P1314 [NOIP 2011 提高组] 聪明的质监员
题目描述
小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1 到 n 逐一编号,每个矿石都有自己的重量 wi 以及价值 vi。检验矿产的流程是:
- 给定 m 个区间 [li,ri];
- 选出一个参数 W;
- 对于一个区间 [li,ri],计算矿石在这个区间上的检验值 yi:
yi=j=li∑ri[wj≥W]×j=li∑ri[wj≥W]vj
其中 j 为矿石编号,[p] 是指示函数,若条件 p 为真返回 1,否则返回 0。
这批矿产的检验结果 y 为各个区间的检验值之和。即:i=1∑myi。
若这批矿产的检验结果与所给标准值 s 相差太多,就需要再去检验另一批矿产。小 T 不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让检验结果尽可能的靠近标准值 s,即使得 ∣s−y∣ 最小。请你帮忙求出这个最小值。
输入格式
第一行包含三个整数 n,m,s,分别表示矿石的个数、区间的个数和标准值。
接下来的 n 行,每行两个整数,中间用空格隔开,第 i+1 行表示 i 号矿石的重量 wi 和价值 vi。
接下来的 m 行,表示区间,每行两个整数,中间用空格隔开,第 i+n+1 行表示区间 [li,ri] 的两个端点 li 和 ri。注意:不同区间可能重合或相互重叠。
输出格式
一个整数,表示所求的最小值。
输入输出样例
输入 #1复制
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
输出 #1复制
10
说明/提示
【输入输出样例说明】
当 W 选 4 的时候,三个区间上检验值分别为 20,5,0,这批矿产的检验结果为 25,此时与标准值 s 相差最小为 10。
【数据范围】
对于 10% 的数据,有 1≤n,m≤10;
对于 30% 的数据,有 1≤n,m≤500;
对于 50% 的数据,有 1≤n,m≤5,000;
对于 70% 的数据,有 1≤n,m≤10,000;
对于 100% 的数据,有 1≤n,m≤2×105,0<wi,vi≤106,0<s≤1012,1≤li≤ri≤n。
实现代码:
cpp
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, m, s;
ll y, ans;
ll w[200010], v[200010];
ll l[200010], r[200010];
ll qzh1[200010], qzh2[200010];
bool check(ll wq) {
y = 0;
memset(qzh1, 0, sizeof(qzh1));
memset(qzh2, 0, sizeof(qzh2));
for (int i = 1; i <= n; i++) {
if (w[i] > wq)
qzh1[i] = qzh1[i - 1] + 1, qzh2[i] = qzh2[i - 1] + v[i];
else
qzh1[i] = qzh1[i - 1], qzh2[i] = qzh2[i - 1];
}
for (int i = 1; i <= m; i++) {
int rrrr = r[i];
int llll = l[i];
y += (qzh1[rrrr] - qzh1[llll - 1]) * (qzh2[rrrr] - qzh2[llll - 1]);
}
if (y > s)
return 1;
else
return 0;
}
int main() {
cin >> n >> m >> s;
for (int i = 1; i <= n; i++)
cin >> w[i] >> v[i];
for (int i = 1; i <= m; i++)
cin >> l[i] >> r[i];
int lll = 1;
int rrr = 2000010;
ans = s;
while (lll <= rrr) {
int mid = lll + (rrr - lll) / 2;
if (check(mid))
lll = mid + 1;
else
rrr = mid - 1;
ans = min(ans, llabs(s - y));
}
cout << ans;
}
P2367 语文成绩
题目背景
语文考试结束了,成绩还是一如既往地有问题。
题目描述
语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行。她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少。你能帮帮她吗?
输入格式
第一行有两个整数 n,p,代表学生数与增加分数的次数。
第二行有 n 个数,a1∼an,代表各个学生的初始成绩。
接下来 p 行,每行有三个数,x,y,z,代表给第 x 个到第 y 个学生每人增加 z 分。
输出格式
输出仅一行,代表更改分数后,全班的最低分。
输入输出样例
输入 #1复制
3 2
1 1 1
1 2 1
2 3 1
输出 #1复制
2
说明/提示
对于 40% 的数据,有 n≤103。
对于 60% 的数据,有 n≤104。
对于 80% 的数据,有 n≤105。
对于 100% 的数据,有 n≤5×106,p≤n,学生初始成绩 ≤100,z≤100。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=5e6+10;
int n,p;
int a[N],b[N];
int main(){
cin>>n>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=a[i]-a[i-1];
}
while(p--){
int x,y,z;
cin>>x>>y>>z;
b[x]+=z;
b[y+1]-=z;
}
int min=99999;
for(int i=1;i<=n;i++){
a[i]=a[i-1]+b[i];
if(min>a[i]) min=a[i];
}
cout<<min;
return 0;
}
P3397 地毯
题目背景
题目描述
在 n×n 的格子上有 m 个地毯。
给出这些地毯的信息,问每个点被多少个地毯覆盖。
输入格式
第一行,两个正整数 n,m。意义如题所述。
接下来 m 行,每行两个坐标 (x1,y1) 和 (x2,y2),代表一块地毯,左上角是 (x1,y1),右下角是 (x2,y2)。
输出格式
输出 n 行,每行 n 个正整数。
第 i 行第 j 列的正整数表示 (i,j) 这个格子被多少个地毯覆盖。
输入输出样例
输入 #1复制
5 3
2 2 3 3
3 3 5 5
1 2 1 4
输出 #1复制
0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1
说明/提示
样例解释
覆盖第一个地毯后:
| 0 | 0 | 0 | 0 | 0 |
|---|---|---|---|---|
| 0 | 1 | 1 | 0 | 0 |
| 0 | 1 | 1 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 |
覆盖第一、二个地毯后:
| 0 | 0 | 0 | 0 | 0 |
|---|---|---|---|---|
| 0 | 1 | 1 | 0 | 0 |
| 0 | 1 | 2 | 1 | 1 |
| 0 | 0 | 1 | 1 | 1 |
| 0 | 0 | 1 | 1 | 1 |
覆盖所有地毯后:
| 0 | 1 | 1 | 1 | 0 |
|---|---|---|---|---|
| 0 | 1 | 1 | 0 | 0 |
| 0 | 1 | 2 | 1 | 1 |
| 0 | 0 | 1 | 1 | 1 |
| 0 | 0 | 1 | 1 | 1 |
数据范围
对于 20% 的数据,有 n≤50,m≤100。
对于 100% 的数据,有 n,m≤1000。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int ac[N][N];
int n,m;
int main(){
cin>>n>>m;
while(m--){
int x,y,a,b;
cin>>x>>y>>a>>b;
for(int i=x;i<=a;i++){
for(int j=y;j<=b;j++){
ac[i][j]++;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<ac[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
P1496 火烧赤壁
题目背景
曹操平定北方以后,公元 208 年,率领大军南下,进攻刘表。他的人马还没有到荆州,刘表已经病死。他的儿子刘琮听到曹军声势浩大,吓破了胆,先派人求降了。
孙权任命周瑜为都督,拨给他三万水军,叫他同刘备协力抵抗曹操。
隆冬的十一月,天气突然回暖,刮起了东南风。
没想到东吴船队离开北岸大约二里距离,前面十条大船突然同时起火。火借风势,风助火威。十条火船,好比十条火龙一样,闯进曹军水寨。那里的船舰,都挤在一起,又躲不开,很快地都烧起来。一眨眼工夫,已经烧成一片火海。
曹操气急败坏的把你找来,要你钻入火海把连环线上着火的船只的长度统计出来!
题目描述
给定每个起火部分的起点和终点,请你求出燃烧位置的长度之和。
输入格式
第一行一个整数,表示起火的信息条数 n。
接下来 n 行,每行两个整数 a,b,表示一个着火位置的起点和终点(注意:左闭右开)。
输出格式
输出一行一个整数表示答案。
输入输出样例
输入 #1复制
3
-1 1
5 11
2 9
输出 #1复制
11
说明/提示
数据规模与约定
对于全部的测试点,保证 1≤n≤2×104,−231≤a<b<231,且答案小于 231。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=2e4+10;
int a[N],b[N];
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);
int sum=0;
for(int i=1;i<=n;i++){
sum+=b[i]-a[i];
if(i<n){
if(b[i]>a[i+1]){
sum-=b[i]-a[i+1];
}
}
}
cout<<sum;
return 0;
}