编程学习笔记:
1.D - 地毯(二维差分)
Description
在 n×n_n_×_n_ 的格子上有 m_m_ 个地毯。
给出这些地毯的信息,问每个点被多少个地毯覆盖。
Input
第一行,两个正整数 n,m_n_,m。意义如题所述。
接下来 m_m_ 行,每行两个坐标 (x1,y1)(_x_1,_y_1) 和 (x2,y2)(_x_2,_y_2),代表一块地毯,左上角是 (x1,y1)(_x_1,_y_1),右下角是 (x2,y2)(_x_2,_y_2)。
Output
输出 n_n_ 行,每行 n_n_ 个正整数。
第 i_i_ 行第 j_j_ 列的正整数表示 (i,j)(i,j) 这个格子被多少个地毯覆盖。
Sample 1
Inputcopy | Outputcopy |
---|---|
``` | |
5 3 | |
2 2 3 3 | |
3 3 5 5 | |
1 2 1 4 |
| ```
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
|
Hint
样例解释
覆盖第一个地毯后:
00 | 00 | 00 | 00 | 00 |
---|---|---|---|---|
00 | 11 | 11 | 00 | 00 |
00 | 11 | 11 | 00 | 00 |
00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 |
覆盖第一、二个地毯后:
00 | 00 | 00 | 00 | 00 |
---|---|---|---|---|
00 | 11 | 11 | 00 | 00 |
00 | 11 | 22 | 11 | 11 |
00 | 00 | 11 | 11 | 11 |
00 | 00 | 11 | 11 | 11 |
覆盖所有地毯后:
00 | 11 | 11 | 11 | 00 |
---|---|---|---|---|
00 | 11 | 11 | 00 | 00 |
00 | 11 | 22 | 11 | 11 |
00 | 00 | 11 | 11 | 11 |
00 | 00 | 11 | 11 | 11 |
数据范围
对于 20%20% 的数据,有 n≤50_n_≤50,m≤100_m_≤100。
对于 100%100% 的数据,有 n,m≤1000_n_,_m_≤1000。
【暴力循环】
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int maxn = 1e3 + 100;
int a[maxn][maxn] = { 0 };
signed main() {
int n, m; cin >> n >> m;
for (int i = 1; i <= m; i++) {
int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
for (int j = x1; j <= x2; j++) {
for (int k = y1; k <= y2; k++) {
a[j][k]++;
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << a[i][j] << " ";
}
cout << endl;
}
return 0;
}
【二维差分】
b[i][j] = a[i][j] - a[i][j-1] - a[i-1][j] + a[i-1][j-1]
要修改的话就直接:b[x1][y1]++ b[x2+1][y1]-- b[x1][y2+1]-- b[x2+1][y2+1]++
就当正常的差分来用就行了,没啥特别的
#include<cstdio>
#include<iostream>
using namespace std;
int cf[2000][2000];
int main(){
int n,m;
int x1,y1,x2,y2;
scanf("%d%d",&n,&m);
while(m--)//循环的另一种方式(m==0时,会自动跳出,所以循环m遍)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
for(int i=x1;i<=x2;i++)//每一行进行差分,时间复杂度O(N)
{
cf[i][y1]++;
cf[i][y2+1]--;//差分
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cf[i][j]+=cf[i][j-1];//复原,利用A[I]-A[I-1]=B[I],可以推得B[I]+A[I-1]=A[I](因为B[I-1]已被复原为A[I-1],所以得到此递推公式)
printf("%d ",cf[i][j]);//复原后输出
}
printf("\n");//换行
}
return 0;
}
2.B - 最大加权矩形(二维前缀)
Description
为了更好的备战 NOIP2013,电脑组的几个女孩子 LYQ,ZSC,ZHQ 认为,我们不光需要机房,我们还需要运动,于是就决定找校长申请一块电脑组的课余运动场地,听说她们都是电脑组的高手,校长没有马上答应他们,而是先给她们出了一道数学题,并且告诉她们:你们能获得的运动场地的面积就是你们能找到的这个最大的数字。
校长先给他们一个 n×n_n_×_n_ 矩阵。要求矩阵中最大加权矩形,即矩阵的每一个元素都有一权值,权值定义在整数集上。从中找一矩形,矩形大小无限制,是其中包含的所有元素的和最大 。矩阵的每个元素属于 [−127,127][−127,127] ,例如
0 –2 –7 0
9 2 –6 2
-4 1 –4 1
-1 8 0 –2
Copy
Plain text
在左下角:
9 2
-4 1
-1 8
Copy
Plain text
和为 1515。
几个女孩子有点犯难了,于是就找到了电脑组精打细算的 HZH,TZY 小朋友帮忙计算,但是遗憾的是他们的答案都不一样,涉及土地的事情我们可不能含糊,你能帮忙计算出校长所给的矩形中加权和最大的矩形吗?
Input
第一行:n_n_,接下来是 n_n_ 行 n_n_ 列的矩阵。
Output
最大矩形(子矩阵)的和。
Sample 1
Inputcopy | Outputcopy |
---|---|
``` | |
4 | |
0 -2 -7 0 | |
9 2 -6 2 | |
-4 1 -4 1 | |
-1 8 0 -2 |
| 15 |
Hint
1≤n≤1201≤_n_≤120
Sponsor
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int maxn = 1e3 + 100;
int a[maxn][maxn];
int sum[maxn][maxn];
signed main() {
int n; cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] + a[i][j] - sum[i - 1][j - 1];
}
}
int maxx = -10000;
for (int x1 = 1; x1 <= n; x1++) {
for (int y1 = 1; y1 <= n; y1++) {
for (int x2 = x1; x2 <= n; x2++) {
for (int y2 = y1; y2 <= n; y2++) {
int s = sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1];
maxx = max(maxx, s);
}
}
}
}
cout << maxx << endl;
return 0;
}
如果没用二维差分而是直接暴力循环,包超时的
3.E - 海底高铁
Description
该铁路经过 N_N_ 个城市,每个城市都有一个站。不过,由于各个城市之间不能协调好,于是乘车每经过两个相邻的城市之间(方向不限),必须单独购买这一小段的车票。第 i_i_ 段铁路连接了城市 i_i_ 和城市 i+1(1≤i<N)i+1(1≤_i_<N)。如果搭乘的比较远,需要购买多张车票。第 i_i_ 段铁路购买纸质单程票需要 Ai_Ai_ 博艾元。
虽然一些事情没有协调好,各段铁路公司也为了方便乘客,推出了 IC 卡。对于第 i_i_ 段铁路,需要花 Ci_Ci_ 博艾元的工本费购买一张 IC 卡,然后乘坐这段铁路一次就只要扣 Bi(Bi<Ai)Bi(Bi<Ai) 元。IC 卡可以提前购买,有钱就可以从网上买得到,而不需要亲自去对应的城市购买。工本费不能退,也不能购买车票。每张卡都可以充值任意数额。对于第 i_i_ 段铁路的 IC 卡,无法乘坐别的铁路的车。
Uim 现在需要出差,要去 M_M_ 个城市,从城市 P1_P_1 出发分别按照 P1,P2,P3,⋯,PM_P_1,_P_2,_P_3,⋯,PM 的顺序访问各个城市,可能会多次访问一个城市,且相邻访问的城市位置不一定相邻,而且不会是同一个城市。
现在他希望知道,出差结束后,至少会花掉多少的钱,包括购买纸质车票、买卡和充值的总费用。
Input
第一行两个整数,N,M_N_,M。
接下来一行,M_M_ 个数字,表示 Pi_Pi_。
接下来 N−1_N_−1 行,表示第 i_i_ 段铁路的 Ai,Bi,Ci_Ai_,Bi,Ci。
Output
一个整数,表示最少花费
Sample 1
Inputcopy | Outputcopy |
---|---|
``` | |
9 10 | |
3 1 4 1 5 9 2 6 5 3 | |
200 100 50 | |
300 299 100 | |
500 200 500 | |
345 234 123 | |
100 50 100 | |
600 100 1 | |
450 400 80 | |
2 1 10 |
| 6394 |
Hint
22 到 33 以及 88 到 99 买票,其余买卡。
对于 30%30% 数据 M=2_M_=2。
对于另外 30%30% 数据 N≤1000,M≤1000_N_≤1000,M_≤1000。
对于 100%100% 的数据 M,N≤105,Ai,Bi,Ci≤105_M,_N_≤105,Ai,Bi,_Ci_≤105。
Sponsor
【超时代码】
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int maxn = 1e5 + 100;
struct x {
int a, b, c;
int flag = 0;
};
int p[maxn];
int pnum[maxn] = { 0 };
int pa[maxn] = { 0 };
int pb[maxn] = { 0 };
int z[maxn] = { 0 };
x s[maxn];
signed main() {
int n, m; cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> p[i];
if (i >= 2) {
int i1 = p[i - 1];
int i2 = p[i];
if (i1 < i2) {
for (int k = i1; k < i2; k++) {
pnum[k]++;
}
}
else {
for (int k = i2; k < i1; k++) {
pnum[k]++;
}
}
}
}
for (int i = 1; i <= n - 1; i++) {
cin >> s[i].a >> s[i].b >> s[i].c;
pa[i] = pa[i - 1] + s[i].a;//前缀
pb[i] = pb[i - 1] + s[i].b;
}
for (int i = 1; i <= n - 1; i++) {
int suma = pnum[i] * s[i].a;
int sumb = pnum[i] * s[i].b + s[i].c;
if (suma > sumb) {
s[i].flag = 1;
z[i] += s[i].b - s[i].a;
}
}
/*for (int i = 1; i <= n - 1; i++) {
z[i] = z[i] + z[i - 1];
pa[i] = pa[i] + z[i];
}*/
int money = 0;
for (int i = 1; i <= n - 1; i++) {
if (s[i].flag == 1) {
money += s[i].c;
money += s[i].b * pnum[i];
}
else {
money += s[i].a * pnum[i];
}
}
cout << money << endl;
return 0;
}
【运行成功】
上面那段代码超时,主要是因为判断是否需要办办证那块地方出了问题,我刚开始没注意,就用了二重循环,后来改成差分就可以了,只需要循环一次,将差分好的数据添加回去前记得要将差分数组变回原数组,这道题也就题目有点长,其他的没啥
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int maxn = 1e5 + 100;
struct x {
int a, b, c;
int flag = 0;
};
int p[maxn];
int pnum[maxn] = { 0 };
int pnumz[maxn] = { 0 };
x s[maxn];
signed main() {
int n, m; cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> p[i];
if (i >= 2) {
int i1 = p[i - 1];
int i2 = p[i];
if (i1 < i2) {
pnumz[i1]++;
pnumz[i2]--;
}
else {
pnumz[i2]++;
pnumz[i1]--;
}
}
}
for (int i = 1; i <= n; i++) {
pnumz[i] = pnumz[i] + pnumz[i - 1];
pnum[i] = pnumz[i];
}
for (int i = 1; i <= n - 1; i++) {
cin >> s[i].a >> s[i].b >> s[i].c;
}
for (int i = 1; i <= n - 1; i++) {
int suma = pnum[i] * s[i].a;
int sumb = pnum[i] * s[i].b + s[i].c;
if (suma > sumb) {
s[i].flag = 1;
}
}
int money = 0;
for (int i = 1; i <= n - 1; i++) {
if (s[i].flag == 1) {
money += s[i].c;
money += s[i].b * pnum[i];
}
else {
money += s[i].a * pnum[i];
}
}
cout << money << endl;
return 0;
}
4.G - Tallest Cow
FJ’s N (1 ≤ N ≤ 10,000) cows conveniently indexed 1…N are standing in a line. Each cow has a positive integer height (which is a bit of secret). You are told only the height H (1 ≤ H ≤ 1,000,000) of the tallest cow along with the index I of that cow.
FJ has made a list of _R _(0 ≤ R ≤ 10,000) lines of the form “cow 17 sees cow 34”. This means that cow 34 is at least as tall as cow 17, and that every cow between 17 and 34 has a height that is strictly smaller than that of cow 17.
For each cow from 1…N, determine its maximum possible height, such that all of the information given is still correct. It is guaranteed that it is possible to satisfy all the constraints.
Input
Line 1: Four space-separated integers: N, I, H and R
Lines 2…R+1: Two distinct space-separated integers A and B (1 ≤ A, B ≤ N), indicating that cow A can see cow B.
Output
Lines 1…N: Line i contains the maximum possible height of cow i.
Sample
Inputcopy | Outputcopy |
---|---|
``` | |
9 3 5 5 | |
1 3 | |
5 3 | |
4 3 | |
3 7 | |
9 8 |
| ```
5
4
5
3
4
4
5
5
5
|
Sponsor
【运行失败】(没有去重)
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
#define int long long
#define double long double
const int maxn = 1e5 + 100;
int s[maxn] = { 0 };
signed main() {
int n, I, h, r; cin >> n >> I >> h >> r;
// 几头 编号 高度 行
while (r--) {
int a, b;
cin >> a >> b;
if (a > b) {
swap(a, b);
}
s[a+1]--; s[b]++;
}
for (int i = 1; i <= n; i++) {
s[i] = s[i] + s[i - 1];
cout << s[i] + h << "\n";
}
return 0;
}
【运行成功】
很巧妙地使用了一个map 来进行去重操作
一个元素是 pair(int,int) 访问时使用 make_pair(int,int) 查找即可
另一个元素是一个 bool 类型 用来判断有没有重复
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
#define int long long
#define double long double
const int maxn = 1e5 + 100;
map<pair<int, int>, bool>cow;
int s[maxn] = { 0 };
signed main() {
int n, I, h, r; cin >> n >> I >> h >> r;
// 几头 编号 高度 行
while (r--) {
int a, b;
cin >> a >> b;
if (a > b) {
swap(a, b);
}
if (cow[make_pair(a, b)]) {
continue;
}
s[a+1]--; s[b]++;
cow[make_pair(a, b)] = true;
}
for (int i = 1; i <= n; i++) {
s[i] = s[i] + s[i - 1];
cout << s[i] + h << "\n";
}
return 0;
}
5.E - 学生分组
Description
有 n_n_ 组学生,给出初始时每组中的学生个数,再给出每组学生人数的上界 R_R_ 和下界 L (L≤R)L (L_≤_R),每次你可以在某组中选出一个学生把他安排到另外一组中,问最少要多少次才可以使 N_N_ 组学生的人数都在 [L,R][L,R] 中。
Input
第一行一个整数 n_n_,表示学生组数;
第二行 n_n_ 个整数,表示每组的学生个数;
第三行两个整数 L,R_L_,R,表示下界和上界。
Output
一个数,表示最少的交换次数,如果不能满足题目条件输出 −1−1。
Sample 1
Inputcopy | Outputcopy |
---|---|
``` | |
2 | |
10 20 | |
10 15 |
| 5 |
Hint
数据范围及约定
对于全部数据,保证 1≤n≤501≤_n_≤50。
#include<bits/stdc++.h>
using namespace std;
int n,a[51],b,c,all,l,r,ans;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
cin>>l>>r;
for(int i=1;i<=n;i++)
{
all+=a[i];
}
if(all<n*l||all>n*r)
{
cout<<"-1";
return 0;
}
for(int i=1;i<=n;i++)
{
if(a[i]<l)
{
b+=(l-a[i]);
}
if(a[i]>r) c+=(a[i]-r);
}
cout<<max(b,c);
return 0;
}
我刚开始是用双指针来写的,有点画蛇添足了,当时的我先把每组的学生升序快排,然后一个指针指向最前面,一个指针指向最后面,然后两个被指向的组进行交换,满足条件后移动指针,最后再统计一下剩余的组里(不符合要求的)还有多少学生需要交换。
但其实根本没有必要,反正多出来的或者少出来的组的学生都是要被交换的,那么把他们单独统计一下有多少然后再选小的那个就OK了。
6.K - k倍区间(from 蓝桥杯)
Description
给定一个长度为 N_N_ 的数列,A1,A2,⋯AN_A_1,A_2,⋯_AN,如果其中一段连续的子序列 Ai,Ai+1,⋯Aj(i≤j)Ai,Ai+1,⋯_Aj_(i_≤_j) 之和是 K_K_ 的倍数,我们就称这个区间 [i,j][i,j] 是 K_K_ 倍区间。
你能求出数列中总共有多少个 K_K_ 倍区间吗?
Input
第一行包含两个整数 N_N_ 和 KMATH:0(1≤Ai≤105)KMATH:0(1≤_Ai_≤105)。
Output
输出一个整数,代表 K_K_ 倍区间的数目。
Sample 1
Inputcopy | Outputcopy |
---|---|
``` | |
5 2 | |
1 | |
2 | |
3 | |
4 | |
5 |
| 6 |
Hint
时限 2 秒, 256M。蓝桥杯 2017 年第八届
Sponsor
【超时】
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int maxn = 1e6 + 100;
int s[maxn] = { 0 };
signed main() {
int n,k; cin >> n >> k;
for (int i = 1; i <= n; i++) {
int tmp; cin >> tmp;
s[i] = s[i-1] + tmp;
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
int sum = s[j] - s[i - 1];
if (sum % k == 0) {
cnt++;
}
}
}
cout << cnt;
return 0;
}
【运行成功】
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int maxn = 1e6 + 100;
int s[maxn] = { 0 };
int s1[maxn] = { 0 };
signed main() {
int n, k; cin >> n >> k;
int len = 1; int I = 0;
for (int i = 1; i <= n; i++) {
int tmp; cin >> tmp;
s[i] = s[i - 1] + tmp;
s1[s[i] % k]++;
}
s1[0]++;
int cnt = 0;
for (int i = 0; i <= k; i++) {
cnt += s1[i]*(s1[i]-1)/2;
}
cout << cnt;
return 0;
}
感觉这道题蛮有意思的(把我写红温了),仔细看看代码就知道了,用了下排列组合