萌新妹子求帅哥带,代码清新可爱,不知道哪里错了,求调~(悬15鱼币 10 贡献)
本帖最后由 zhangjinxuan 于 2024-3-6 08:52 编辑Never gonna give you up. Never gonna let you down.
题目链接:https://www.luogu.com.cn/problem/P9912
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, Q, a, ans, rem;
map<int, vector<int>> pos;
struct Query {
int l, r, x, id;
} q;
map<int, vector<Query>> qs;
struct Info {
int tot, lh, rh; // 最左边有没有块,最右边有没有块
};
Info add(const Info a, const Info b){ // 注意该运算不满足交换律
if (a.tot + a.lh + a.rh == -3) return b;
if (b.tot + b.lh + b.rh == -3) return a;
return {a.tot + b.tot - (a.rh && b.lh), a.lh, b.rh};
}
namespace SGT {
Info res;
#define ls(i) (i << 1)
#define rs(i) (i << 1 | 1)
void build(int i, int l, int r) {
if (l == r) return res = {1, 1, 1}, void();
int mid = (l + r) / 2; build(ls(i), l, mid); build(rs(i), mid + 1, r);
res = add(res, res);
}
void update(int i, int l, int r, int qp) {
if (l > qp || r < qp) return; int mid = (l + r) / 2;
if (l == r && l == qp) return res = {0, 0, 0}, void();
update(ls(i), l, mid, qp); update(rs(i), mid + 1, r, qp);
res = add(res, res);
}
Info query(int i, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return {-1, -1, -1};
//printf("%d %d %d %d %d\n", l, r, res.tot, res.lh, res.rh);
if (l >= ql && r <= qr) return res;
int mid = (l + r) / 2;
return add(query(ls(i), l, mid ,ql, qr), query(rs(i), mid + 1, r, ql, qr));
}
}
signed main() {
scanf("%lld%lld", &n, &Q);
vector<int> qss;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a);
pos].push_back(i);
qss.push_back(a);
}
sort(qss.begin(), qss.end());
auto enit = unique(qss.begin(), qss.end());
int minv = 1ll << 60;
for (int i = 1; i <= Q; ++i) {
scanf("%lld%lld%lld", &q.l, &q.r, &q.x);
if (q.x < qss) q.x = -1;
else q.x = qss.x + 1) - qss.begin() - 1), qss.size() - 1)];
q.id = i; qs.x].push_back(q);
minv = min(minv, q.x);
}
for (int i = 1; i <= n; ++i) {
if (a < minv) pos.push_back(i);
}
SGT::build(1, 1, n);
for (auto i : qs) {
for (auto j : pos) {
if (rem) continue;
rem = 1;
SGT::update(1, 1, n, j);
}
for (auto j : i.second) {
Info tmp = SGT::query(1, 1, n, j.l, j.r);
ans = tmp.tot;
//printf("ans: %lld, %lld, %lld, %lld\n", j.id, tmp.tot, tmp.lh, tmp.rh);
}
}
for (int i = 1; i <= Q; ++i) printf("%lld\n", ans);
}
这是一个离线算法,大体思路如下:
[*]首先所有的询问的 x 参数,把 x 重新赋值为在 a 序列中比他小于等于的最大值,如果不存在就改成 0。
[*]然后按照询问参数 x 从小到大模拟涨水,对于每一次涨水,看看哪些点被淹没了,在线段树中作出对应的修改。
[*]最后查询区间,还原询问。
线段树记录的节点表示目前的岛屿组成极大未淹没连续子段的情况,tot 表示总数,lh 表示这个区间最左边的岛屿是否被淹没,rh 表示这个区间最右边的岛屿是否被淹没。
严重怀疑是第一步写错了,或者是线段树 query 返回 {-1, -1, -1} 这个地方。
第一次尝试写代码,结果全是错的。 FishC 发表于 2024-3-4 17:14
这段代码中存在一些问题:
1. 缺少头文件``和``,需要添加上去。
题目是这样的:
# Zatopljenje
## 题目描述
Mr. Malnar 有一张地形图,上面画着一个区域内每个位置的海拔高度。具体的,有 $n$ 个位置排成一排,第 $i$ 个位置高出海平面 $h_i$ 米。
海平面可能会上升。给定 $q$ 次询问,对于第 $i$ 次询问你需回答:如果海平面高度上升 $x_i$ 米,那么 $$ 区间中会形成多少个岛?一个岛的定义为一个极长的,每个位置的高度都大于 $x_i$ 的段。
![](https://cdn.luogu.com.cn/upload/image_hosting/mffg52xn.png)
上图分别表示了样例 1 的第一组询问以及样例 2 的第二组询问。左图 $$ 区间中有 $,$ 两个岛,而右图中有 $,,,$ 四个岛。
## 输入格式
第一行两个整数 $n,q$。
第二行 $n$ 个整数 $h_{1\sim n}$ 表示每个位置的初始海拔。
接下来 $q$ 行每行 $3$ 个整数 $l_i,r_i,x_i$ 表示一次询问。
## 输出格式
输出 $q$ 行,第 $i$ 行一个整数表示第 $i$ 次询问的答案。
## 样例 #1
### 样例输入 #1
```
6 3
2 4 2 3 4 1
2 5 2
3 5 3
3 4 4
```
### 样例输出 #1
```
2
1
0
```
## 样例 #2
### 样例输入 #2
```
10 3
5 0 3 4 2 0 1 6 3 5
3 9 1
1 10 3
1 10 2
```
### 样例输出 #2
```
2
4
3
```
## 提示
### 数据范围
|$\text{Subtask}$|分值|特殊性质|
|:-:|:-:|:-:|
|$1$|$10$|$n,q\le 2000$|
|$2$|$20$|$l_i=1,r_i=n$|
|$3$|$20$|存在 $p\in $ 满足 $h_1\ge h_2\ge \cdots \ge h_p\le h_{p+1}\le \cdots \le h_n$|
|$4$|$60$|无|
对于所有数据,$1\le n,q\le 2\times 10^5$,$0\le h_i,x_i\le 10^9$,$1\le l_i\le r_i\le n$。 我现在已经改出来了,但是不知道为什么原本的代码是错误的? 这段代码中存在一些问题:
1. 缺少头文件`<map>`和`<cstdio>`,需要添加上去。
2. `namespace SGT`中的函数`query`中缺少返回值,需要添加`return res;`。
3. `main`函数中对数组`pos`进行了操作,需要先声明一个数组才能使用。
4. 在调用`query`函数时,传入参数类型错误。应该传入四个整数作为参数,而不是一个`Query`结构体。
5. `query`函数中存在递归调用,但没有设置终止条件,可能会导致栈溢出。可以在开始处加上判断条件,如果区间范围不合法,则返回`{-1, -1, -1}`。
6. 在递归调用之前,应该先判断是否已经找到了答案,如果已经找到则直接返回结果。
修改后的代码如下:
#include <iostream>
#include <map>
using namespace std;
#define int long long
int n, Q, a, ans, rem;
map<int, int> pos;
struct Query {
int l, r, x, id;
} q;
map<int, Query> qs;
struct Info {
int tot, lh, rh; // 最左边有没有块,最右边有没有块
};
Info add(const Info a, const Info b){ // 注意该运算不满足交换律
if (a.tot + a.lh + a.rh == -3) return b;
if (b.tot + b.lh + b.rh == -3) return a;
return {a.tot + b.tot - (a.rh && b.lh), a.lh, b.rh};
}
namespace SGT {
Info res;
#define ls(i) (i << 1)
#define rs(i) (i << 1 | 1)
void build(int i, int l, int r) {
if (l == r) {
if (!pos) res = {0, -1, -1}; // 空位置
else res = {1, 1, 1}; // 有方块
return;
}
int mid = (l + r) >> 1;
build(ls(i), l, mid);
build(rs(i), mid + 1, r);
res = add(res, res);
}
Info query(int i, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return {-1, -1, -1};
if (l >= ql && r <= qr) return res;
int mid = (l + r) >> 1;
Info res = add(query(ls(i), l, mid, ql, qr), query(rs(i), mid + 1, r, ql, qr));
return res;
}
#undef ls
#undef rs
}
signed main() {
cin >> n >> Q;
for (int i = 1; i <= n; ++i) {
cin >> a;
pos = i;
}
for (int i = 1; i <= Q; ++i) {
cin >> q.l >> q.r >> q.x;
q.id = i;
qs = q;
}
SGT::build(1, 1, n);
for (auto it = qs.begin(); it != qs.end(); ++it) {
Info info = SGT::query(1, 1, n, it->second.l, it->second.r);
int cnt = info.tot + info.lh + info.rh;
if (cnt < 0) continue;
ans += cnt;
SGT::modify(1, 1, n, it->second.l, it->second.r, 0);
}
cout << ans << endl;
return 0;
}
希望能帮到你!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 zhangjinxuan 发表于 2024-03-04 17:15
题目是这样的:
#Zatopljenje
## 题目描述
...
根据题目描述,我们需要对每次询问进行处理。首先,我们可以使用一个数组`h`来保存每个位置的初始海拔高度。然后,我们逐个处理每次询问。
对于每个询问,我们需要找到海平面上升后形成的岛的数量。为了方便计算,我们可以将问题转化为求解海平面上升前所形成的水域的数量。
我们可以使用一个变量`water_level`来表示当前的海平面高度。初始时,`water_level`的值为0。我们从左到右遍历每个位置,如果该位置的海拔高度大于当前的`water_level`,则说明这是一个新的水域的起点,水域的数量加1,并更新`water_level`的值为该位置的海拔高度。如果该位置的海拔高度小于等于当前的`water_level`,则说明这是一个水域的一部分,不需要做任何操作。
接下来,我们需要根据每次询问给定的上升高度来计算岛的数量。假设当前的`water_level`为0,我们需要找到在海平面上升`xi`米后形成的岛的数量。我们可以使用二分搜索的方法来找到新的水域的起点,找到最后一个海拔高度小于等于`xi + water_level`的位置,将其设置为新的`water_level`,并将其后面的位置视为新的岛。新的岛的数量等于区间的长度减去最后一个水域的起点位置。
根据以上的思路,我们可以实现以下代码:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<int> h(n); // 保存每个位置的初始海拔高度
for (int i = 0; i < n; i++) {
cin >> h;
}
int water_level = 0; // 当前海平面高度
// 遍历每次询问
for (int i = 0; i < q; i++) {
int l, r, x;
cin >> l >> r >> x;
// 计算海平面上升后形成的岛的数量
int islands = 0;
// 二分搜索找到新的水域的起点
int low = l - 1, high = r - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (h <= x + water_level) {
low = mid + 1;
} else {
high = mid - 1;
}
}
// 更新水域的起点为最后一个小于等于 x+water_level 的位置
if (low < n && h <= x + water_level) {
water_level = h;
islands = r - low - 1; // 岛的数量等于区间的长度减去水域的起点位置
}
cout << islands << endl;
}
return 0;
}
希望以上解答对你有帮助,如果还有其他问题,请随时提问。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 捞 zhangjinxuan 发表于 2024-03-05 13:33
捞
对不起,我之前的回答并没有给出完整的代码。以下是完整的代码实现:
#include <iostream>
#include <vector>
using namespace std;
// 找到最后一个小于等于target的位置
int binary_search(vector<int>& nums, int target) {
int low = 0, high = nums.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return high;
}
int main() {
int n, q;
cin >> n >> q;
vector<int> h(n); // 保存每个位置的初始海拔高度
for (int i = 0; i < n; i++) {
cin >> h;
}
int water_level = 0; // 当前海平面高度
// 遍历每次询问
for (int i = 0; i < q; i++) {
int l, r, x;
cin >> l >> r >> x;
// 计算海平面上升后形成的岛的数量
int islands = 0;
// 二分搜索找到新的水域的起点
int start = binary_search(h, x + water_level);
// 新的water_level为x + water_level
water_level += x;
// 计算岛的数量
islands = r - start - 1;
cout << islands << endl;
}
return 0;
}
希望这次的回答能够帮助到你!如果还有其他问题,请随时提问。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 zhangjinxuan 发表于 2024-03-06 08:41
我现在已经改出来了,但是不知道为什么原本的代码是错 ...
对不起,我之前的回答没有详细解释原始代码中的错误。以下是对原始代码错误的解释:
1. 缺少头文件 `#include <iostream>` 和 `#include <vector>`:这两个头文件分别用于输入输出和使用向量容器。
2. 没有正确处理每次询问的输入:原始代码中的输入方式不正确。每次询问涉及三个整数,应该使用 `cin` 来读取这三个整数。例如,`cin >> l >> r >> x;`。
3. 没有计算初始水域数量:在遍历每个位置之前,应该初始化 `int islands = 1;`,以便将第一个海拔高度大于当前水位的位置视为一个新的水域。
4. 未更新 `water_level` 的值:在找到新的水域起点后,需要将 `water_level` 的值更新为新的水位,即 `water_level += x;`。
5. 未输出每次询问的结果:在计算完每次询问的岛屿数量后,需要使用 `cout` 输出结果,例如 `cout << islands << endl;`。
通过以上的修改,可以得到正确的代码实现。抱歉给你带来了困扰。如果你有任何其他问题,请随时告诉我。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 捞 zhangjinxuan 发表于 2024-03-07 12:55
捞
非常抱歉,之前的回答还是有问题。我再次回顾一下原始代码,并给出修正后的版本。
根据题目描述,我们需要计算每次询问中上升海平面后形成的岛屿数量。具体思路如下:
1. 遍历每次询问,获取查询范围和上升高度。
2. 根据查询范围,计算初始水域的数量 `islands`,初始值为1(因为第一个位置一定是水域)。
3. 初始化当前的水位 `water_level` 为0。
4. 遍历查询范围内的每个位置:
- 如果该位置的海拔高度大于当前水位 `water_level`,说明遇到了新的水域,将 `islands` 加1,并更新 `water_level` 为该位置的海拔高度。
- 否则,继续遍历下一个位置。
5. 在计算完每次询问的岛屿数量后,输出结果。
以下是修正后的代码实现:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<int> h(n); // 保存每个位置的初始海拔高度
for (int i = 0; i < n; i++) {
cin >> h;
}
// 遍历每次询问
for (int i = 0; i < q; i++) {
int l, r, x;
cin >> l >> r >> x;
int islands = 1; // 初始水域数量
int water_level = 0; // 当前水位
// 计算初始水域数量和当前水位
for (int j = l - 1; j < r; j++) {
if (h > water_level) {
islands++;
water_level = h;
}
}
// 输出结果
cout << islands - 1 << endl;
}
return 0;
}
希望这次能够解决你的问题,如果还有其他疑问,请随时提问。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 我这代码都超时,你那肯定超
不过这代码除了tle就是ac
代码应该没有问题,接下来看看要如何提高代码效率了
这洛谷最恶心的一点就是,他只告诉你错了,但是不告诉你在哪个数据上错了
这还调试个球
你只能去猜自己代码中可能的问题,没法调试
如果可以,换一个刷题网站吧
这洛谷太tm垃圾了
/*
#include <iostream>
using std::cin, std::cout, std::endl;
int find(int n, const int *h, int l, int r, int x) {
--l, --r;
int count = 0;
while(true) {
while(l <= r) if(h > x) goto e;
return count;
e: while(l <= r) if(h <= x) break;
++count;
}
}
int main() {
int n, q; cin >> n >> q;
int h; for(int i = 0; i < n; ++i) cin >> h;
for(int i = 0; i < q; ++i) {
int l, r, x;
cin >> l >> r >> x;
int count = find(n, h, l, r, x);
cout << count << endl;
}
return 0;
}
*/
/*
#include <iostream>
using std::cin, std::cout, std::endl;
int n, q, h, l, r, x;
int count, i;
void find() {
--l, --r;
count = 0;
while(true) {
while(l <= r) if(h > x) goto e;
return;
e: while(l <= r) if(h <= x) break;
++count;
}
}
int main() {
cin >> n >> q;
for(i = 0; i < n; ++i) cin >> h;
for(i = 0; i < q; ++i) {
cin >> l >> r >> x;
find(); cout << count << endl;
}
return 0;
}
*/
#include <cstdio>
using std::scanf, std::printf;
int n, q, h, l, r, x;
int count, i;
__attribute((always_inline)) inline void find() {
--l, --r;
count = 0;
while(true) {
while(l <= r) if(h > x) goto e;
return;
e: while(l <= r) if(h <= x) break;
++count;
}
}
int main() {
scanf("%d%d", &n, &q);
for(i = 0; i < n; ++i) scanf("%d", &h);
for(i = 0; i < q; ++i) {
scanf("%d%d%d", &l, &r, &x);
find(); printf("%d\n", count);
}
return 0;
}
第一个代码写的多好,非要为了执行效率,把代码写的连你自己都看不懂了,这就好?
本帖最后由 人造人 于 2024-3-7 18:21 编辑
他这数据还不是一次性给出?
把代码都写成这样了,还是不行
我这扫描一遍都超时?
这个题有更好的算法?
tm的,他不给输入数据,我不知道我的程序在什么数据上出错了,没法调试
我不玩了,谁爱玩谁去玩吧
/*
#include <iostream>
using std::cin, std::cout, std::endl;
int find(int n, const int *h, int l, int r, int x) {
--l, --r;
int count = 0;
while(true) {
while(l <= r) if(h > x) goto e;
return count;
e: while(l <= r) if(h <= x) break;
++count;
}
}
int main() {
int n, q; cin >> n >> q;
int h; for(int i = 0; i < n; ++i) cin >> h;
for(int i = 0; i < q; ++i) {
int l, r, x;
cin >> l >> r >> x;
int count = find(n, h, l, r, x);
cout << count << endl;
}
return 0;
}
*/
/*
#include <iostream>
using std::cin, std::cout, std::endl;
int n, q, h, l, r, x;
int count, i;
void find() {
--l, --r;
count = 0;
while(true) {
while(l <= r) if(h > x) goto e;
return;
e: while(l <= r) if(h <= x) break;
++count;
}
}
int main() {
cin >> n >> q;
for(i = 0; i < n; ++i) cin >> h;
for(i = 0; i < q; ++i) {
cin >> l >> r >> x;
find(); cout << count << endl;
}
return 0;
}
*/
/*
#include <cstdio>
using std::scanf, std::printf;
int n, q, h, l, r, x;
int count, i;
__attribute((always_inline)) inline void find() {
--l, --r;
count = 0;
while(true) {
while(l <= r) if(h > x) goto e;
return;
e: while(l <= r) if(h <= x) break;
++count;
}
}
int main() {
scanf("%d%d", &n, &q);
for(i = 0; i < n; ++i) scanf("%d", &h);
for(i = 0; i < q; ++i) {
scanf("%d%d%d", &l, &r, &x);
find(); printf("%d\n", count);
}
return 0;
}
*/
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
#include <unistd.h>
int n, q, h, l, r, x;
int count, i;
#define ISIZE 0xf00000000
char *ibuf;
long long int ipos;
#define OSIZE 30
char obuf;
int opos;
__attribute((always_inline)) inline void find() {
--l, --r;
count = 0;
while(true) {
while(l <= r) if(h > x) goto e;
return;
e: while(l <= r) if(h <= x) break;
++count;
}
}
__attribute((always_inline)) inline void print_count() {
opos = OSIZE - 2; obuf = '0';
while(count) {
obuf = count % 10 + '0';
count /= 10;
}
write(1, &obuf, OSIZE - opos);
}
__attribute((always_inline)) inline void read_number(int *n) {
while(!isdigit(ibuf)) ++ipos;
*n = 0;
while(isdigit(ibuf)) *n = *n * 10 + (ibuf - '0');
}
int main() {
ibuf = sbrk(ISIZE);
if(ibuf == (void *)-1) return -1;
obuf = '\n';
read(0, ibuf, ISIZE);
read_number(&n);
read_number(&q);
for(i = 0; i < n; ++i) read_number(&h);
for(i = 0; i < q; ++i) {
read_number(&l); read_number(&r); read_number(&x);
find(); print_count();
}
return 0;
}
人造人 发表于 2024-3-7 18:17
{:10_250:}{:10_250:}洛谷某些打印输出题用C做,自己测试输出完全没问题,但是放洛谷上就全WA,一改C++就过了 yinda_peng 发表于 2024-3-7 19:30
洛谷某些打印输出题用C做,自己测试输出完全没问题,但是放洛谷上就全WA,一改C++就 ...
主要是他不给看出错的数据,没法调试 人造人 发表于 2024-3-7 19:41
主要是他不给看出错的数据,没法调试
对,虽然可以下载第一个出错数据,但限制次数,不理解这样做的意义,力扣就好多了 yinda_peng 发表于 2024-3-7 19:45
对,虽然可以下载第一个出错数据,但限制次数,不理解这样做的意义,力扣就好多了
我这边都没有下载第一个的选项
自己作死就一定会死
他这么做还有谁会用他
谁也不用他,他就死了
人造人 发表于 2024-3-7 20:00
我这边都没有下载第一个的选项
自己作死就一定会死
他这么做还有谁会用他
洛谷是这样的,COCI 都公开数据了这个洛谷都不让你下,不知道为什么。
我的代码考虑离线询问,从小到大模拟涨水的过程,对每一次淹没的点更新答案,并使用线段树查询,可以达到比双重循环更优秀的效果,但是不知道为什么这样写错了,只有从大到小才是对的。 差不多得了 zhangjinxuan 发表于 2024-3-8 08:16
差不多得了
说到线段树,我再研究研究,再试试
我又有一个想法,只是在合并区间这里有点问题,我需要再研究研究
^_^
页:
[1]
2