目录
试题F:递增三元组
【问题描述】
【输入格式】
【输出格式】
【样例输入】
【样例输出】
27
【代码解析】
暴力:三重循环计算所有满足ai < bj < ck的数,时间复杂度为O(),只能通过一半的数据。
排序+二分:对于a,b,c数组进行排序。枚举每一个bj,利用二分思想,求出满足bj > ai的最大下标i,则比bj小的a的元素的数量为i + 1,同理求出满足bi < ck的最小下标k,则比bj大的c的元素的数量为n - k,即对于bj存在 (i + 1) * (n - k)种方案满足题意。时间复杂度为O(nlogn),可以通过所有数据
前缀和:和第二种方法思路一样,运用前缀和的思想求比bi小的a的元素的数量及比bi大的c的元素的数量。时间复杂度为O(n),可以通过所有数据。
排序+二分
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int a[N], b[N], c[N];
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i ++) cin >> a[i];
for(int i = 0; i < n; i ++) cin >> b[i];
for(int i = 0; i < n; i ++) cin >> c[i];
sort(a, a + n);
sort(b, b + n);
sort(c, c + n);
LL res = 0;
for(int i = 0; i < n; i ++)
{
LL l = 0, r = n - 1, n1 = 0, n2 = 0;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(a[mid] < b[i]) l = mid;
else r = mid - 1;
}
if(a[r] < b[i]) n1 = r + 1;
l = 0, r = n - 1;
while(l < r)
{
int mid = l + r >> 1;
if(c[mid] > b[i]) r = mid;
else l = mid + 1;
}
if(c[r] > b[i]) n2 = n - r;
res += (LL)n1 * n2;
}
cout << res;
return 0;
}
前缀和
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int a[N], b[N], c[N];
int cnt[N], as[N], cs[N], s[N];
int n;
int main()
{
cin >> n;
for(int i = 0; i < n; i ++) scanf("%d", &a[i]), a[i] ++;
for(int i = 0; i < n; i ++) scanf("%d", &b[i]), b[i] ++;
for(int i = 0; i < n; i ++) scanf("%d", &c[i]), c[i] ++;
for(int i = 0; i < n; i ++) cnt[a[i]] ++;
for(int i = 1; i < N; i ++) s[i] = s[i - 1] + cnt[i];
for(int i = 0; i < n; i ++) as[i] = s[b[i] - 1];
memset(s, 0, sizeof s);
memset(cnt, 0, sizeof cnt);
for(int i = 0; i < n; i ++) cnt[c[i]] ++;
for(int i = 1; i < N; i ++) s[i] = s[i - 1] + cnt[i];
for(int i = 0; i < n; i ++) cs[i] = s[N - 1] - s[b[i]];
LL res = 0;
for(int i = 0; i < n; i ++)
{
res += (LL)as[i] * cs[i];
}
cout << res;
return 0;
}
试题G:螺旋折线
【问题描述】
【输入格式】
【输出格式】
输出dis(X, Y)
【样例输入】
0 1
【样例输出】
3
【代码解析】
模拟 + 找规律,时间复杂度为O(1),注意输出的数据量可能会爆int范围,记得开long long来存。
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
int main()
{
int x, y;
cin >> x >> y;
if(abs(x) <= y)
{
int n = y;
cout << (LL)(2 * n) * (2 * n - 1) + x - (- n) << endl;
}
else if(x >= abs(y))
{
int n = x;
cout << (LL)(2 * n) * (2 * n) + n - y << endl;
}
else if(abs(y) + 1 >= abs(x) && y < 0)
{
int n = abs(y);
cout << (LL)(2 * n) * (2 * n + 1) + n - x << endl;
}
else
{
int n = abs(x);
cout << (LL)(2 * n - 1) * (2 * n - 1) + y - (- n + 1) << endl;
}
return 0;
}
试题H:日志统计
【问题描述】
【输入格式】
【输出格式】
按从小到大的顺序输出热帖id。每个id一行。
1
2
3
4
5
6
7
8
9
【输入样例】
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
【输出样例】
1
3
【代码解析】
模拟题,用排序+双指针
1、对所有的赞按照时间从小到大排序
2、通过双指针i,j维护长度不大于d的区间,并记录该区间的中所有帖子获得的赞数
#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
const int N = 1e5 + 10;
typedef pair<int, int> PII;
PII logs[N];
bool st[N];
int cnt[N];
int n, d, k;
int main()
{
scanf("%d%d%d", &n, &d, &k);
for(int i = 0; i < n; i ++)
{
int ts, id;
scanf("%d%d", &ts, &id);
logs[i] = {ts, id};
}
sort(logs, logs + n);
for(int i = 0, j = 0; i < n; i ++)
{
int id = logs[i].y;
cnt[id] ++;
while(logs[i].x - logs[j].x >= d)
{
cnt[logs[j].y] --;
j ++;
}
if(cnt[id] >= k) st[id] = true;
}
for(int i = 0; i < N; i ++) if(st[i]) cout << i << endl;
return 0;
}
试题I:全球变暖
【问题描述】
你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:
【输入格式】
第一行包含一个整数N。 (1 <= N <= 1000)
以下N行N列代表一张海域照片。
照片保证第1行、第1列、第N行、第N列的像素都是海洋。
请你计算:依照科学家的预测,照片中会有多少岛屿会被完全淹没。
【输出格式】
一个整数表示答案。
1
2
3
4
5
6
7
8
【输入样例】
7
…
.##…
.##…
…##.
…####.
…###.
…
【输出样例】
1
【代码解析】
遍历所有未遍历过的陆地,通过bfs计算出当前位置连通陆地的数量total,以及被淹没陆地的数量bound,若total == bound表示完整淹没的一个岛屿
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
char g[N][N];
bool st[N][N];
int n;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void bfs(int i, int j, int &total, int &bound)
{
queue<PII> q;
st[i][j] = true;
q.push({i, j});
while(q.size())
{
total ++;
bool is_bound = false;
auto t = q.front();
q.pop();
for(int i = 0; i < 4; i ++)
{
int x = t.x + dx[i], y = t.y + dy[i];
if(x < 0 || x >= n || y < 0 || y >= n) continue;
if(!st[x][y] && g[x][y] == '#')
{
st[x][y] = true;
q.push({x, y});
}
if(g[x][y] == '.')
is_bound = true;
}
if(is_bound == true) bound ++;
}
}
int main()
{
cin >> n;
for(int i = 0; i < n; i ++) cin >> g[i];
int cnt = 0;
for(int i = 0; i < n; i ++)
for(int j = 0; j < n; j ++)
{
if(g[i][j] == '#' && !st[i][j])
{
int bound = 0;
int total = 0;
bfs(i, j, total, bound);
if(total == bound) cnt ++;
}
}
cout << cnt;
return 0;
}
试题J:乘积最大
【问题描述】
【输入格式】
【输出格式】
一个整数,表示答案。
1
2
3
4
5
6
7
8
9
10
【输入样例】
5 3
-100000
-10000
2
100000
10000
【输出样例】
999100009
1
2
3
4
5
6
7
8
9
10
再例如:
【输入样例】
5 3
-100000
-100000
-2
-100000
-100000
【输出样例】
-999999829
【代码解析】
分类讨论+贪心:
情况1:n == k,求出数据的乘积取模即可。
情况2:n < k && k为偶数,分析可知,不论数据如何,总能取到数据的乘积为正数。
情况3:n < k && k为奇数 && 数据不全为负数,分析可知,先取最大的数据,此时即可转换为情况2继续进行操作。
情况4:n < k && k为奇数 && 数据全为负数,分析可知,此时无论如何取,最终答案必为负数,这时我们的贪心思路为先取最大的数据,之后取的k - 1个数据保证其乘积最小,即可求出最大的乘积。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL mod = 1000000009;
const int N = 1e5 + 10;
int a[N];
int n, m;
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i ++) scanf("%d", &a[i]);
sort(a, a + n);
int i = 0, j = n - 1, sign = 1;
LL sum = 1;
if(m % 2)
{
sum *= a[j] % mod;
if(a[j] < 0) sign = -1;
j --;
m --;
}
while(m)
{
if((LL)a[i] * a[i + 1] * sign >= (LL)a[j] * a[j - 1] * sign)
{
sum = sum * a[i] % mod * a[i + 1] % mod;
i += 2;
m -= 2;
}
else
{
sum = sum * a[j] % mod * a[j - 1] % mod;
j -= 2;
m -= 2;
}
}
if(sum > 0)
cout << sum % mod;
else
cout << 0-((0-sum)%mod);
return 0;
}
最后大家有疑问的话可以评论区留言讨论,第一次写博客,有写的不好的地方,不清楚的地方还请多多包涵指正。