P8856 [POI2002] 火车线路 题解
解题思路
不难看出一个起点到终点的订购很像区间加,因此不难联想到线段树。
对于一次订购,我们尝试将 N,否则保留这个操作,输出 T。
为什么是 不然就像我第一次交一样只有 32 分。
Fake AC Code
#include <bits/stdc++.h>
using namespace std;
const int N = 60005;
int n, lim, q, dat[N << 2], tg[N << 2];
inline void pushup(int p){dat[p] = max(dat[p << 1], dat[p << 1 | 1]);}
inline void pushdown(int p, int l, int r){
if (tg[p]){
dat[p << 1] += tg[p]; dat[p << 1 | 1] += tg[p];
tg[p << 1] += tg[p]; tg[p << 1 | 1] += tg[p];
tg[p] = 0;
}
}
void update(int p, int l, int r, int lpos, int rpos, int k){
if (l > rpos || r < lpos) return;
if (lpos <= l && r <= rpos){
dat[p] += k;
tg[p] += k;
return;
}
pushdown(p, l, r);
int mid = (l + r) >> 1;
update(p << 1, l, mid, lpos, rpos, k); update(p << 1 | 1, mid + 1, r, lpos, rpos, k);
pushup(p);
}
int query(int p, int l, int r, int lpos, int rpos){
if (l > rpos || r < lpos) return -2e9;
if (lpos <= l && r <= rpos) return dat[p];
pushdown(p, l, r);
int mid = (l + r) >> 1;
return max(query(p << 1, l, mid, lpos, rpos), query(p << 1 | 1, mid + 1, r, lpos, rpos));
}
int main(){
scanf("%d%d%d", &n, &lim, &q);
int l, r, x;
while (q--){
scanf("%d%d%d", &l, &r, &x);
update(1, 1, n, l, r - 1, x);
if (query(1, 1, n, l, r - 1) > lim){
update(1, 1, n, l, r - 1, -x);
printf("N\n");
}
else printf("T\n");
}
return 0;
}
先别着急复制
因为这道题的毒瘤时限只有 100ms,上面这份代码在我交的时候需要吸氧才能稳过,不然测试点 #10 会有 120ms 左右。
那么我们怎么不开 O2 通过呢?我们发现 update 应该没啥可以优化的了,但是每次真的都需要 query 吗?显然不用。我们只需要考虑整个数列中有没有大于火车座位数量 dat[1] 中,因此 query 就优化到了
新的关键部分
int main(){
while (q--){
scanf("%d%d%d", &l, &r, &x);
update(1, 1, n, l, r - 1, x);
if (dat[1] > lim){//query函数可以直接不定义了
update(1, 1, n, l, r - 1, -x);
printf("N\n");
}
else printf("T\n");
}
}
其实还有一种方法就是保留 query,每次先判断本次订购区间的最大值加上本次要订购的数量是否超过了总座位数,没超过再更新,因为感性认为 T 的个数大概率是少于 N 的,所以效率应该会更高。