目录
B-Gaming_牛客小白月赛54 (nowcoder.com)
C-School_牛客小白月赛54 (nowcoder.com)
思维题。
本题应从区间上每个元素出发,因为一个元素就是一个buff,必须少一个buff才能符合要求,每次会给一个区间的buff加上积分。还要求最大积分,题目就变成了每个buff有一定积分,不能全选,怎么选积分最大。结果就很显然是不选一个积分最小的buff。所以我们用差分+前缀和求出每个buff对应的积分,然后减去一个最小的就行了。
#include<bits/stdc++.h>
using namespace std;
#pragma warning(disable:4996);
#define int long long
#define rep(j,b,e) for(int j=(b);j<=(e);j++)
#define drep(j,e,b) for(int j=(e);j>=(b);j--)
const int N = 2e6 + 10;
int mod = (1e7) + 7;
int T = 1;
int n, m, k;
int sum[N];
signed main() {
#ifndef ONLINE_JUDGE
freopen("out.txt", "w", stdout);
#endif
ios::sync_with_stdio(0); cout.tie(0);
cin >> n >> m;
int Sum = 0;
rep(j, 1, n) {
int l, r, s;
cin >> l >> r >> s;
sum[l] += s;
sum[r + 1] -= s;
Sum += s;
}
int ans = 0;
rep(j, 1, m) {
sum[j] += sum[j - 1];//求拿走每个debuff能获得多少分
ans = max(ans, Sum - sum[j]);//减去一个最小的debuff
}
cout << ans;
return 0;
}
C-School_牛客小白月赛54 (nowcoder.com)
这题的特点在于数据量非常大,抛开数据,将时间转换位分钟。很明显是一个前缀和差分模板题,但是因为数据量也就是区间非常大,h*m=1e13。一般的前缀和+差分无法使用。于是用另一种思路。可以把区间看作一个整体,换算时间为分钟后其实相当于是求一点的值是否为1.而原始的差分是标记整个区间,但是要求和就会超时。而差分标记的本质是给左端点之后的所有数加上一个标记,再给右端点之后的数减去一个标记。那么我们查看这个点是否被标记,只需要看这个点之前的正标记数是否大于负标记数,如果正标记多,说明这个点被标记了。反正则无。
所以我们可以将标记的区间存下来,并且升序排序,然后每次询问的时候二分查找正负标记数,判断是否相抵消。
#include<bits/stdc++.h>
using namespace std;
#pragma warning(disable:4996);
#define int long long
#define rep(j,b,e) for(int j=(b);j<=(e);j++)
#define drep(j,e,b) for(int j=(e);j>=(b);j--)
const int N = 2e6 + 10;
int mod = (1e7) + 7;
int T = 1;
int n, m, k;
vector<int>up;
vector<int>down;
signed main() {
#ifndef ONLINE_JUDGE
freopen("out.txt", "w", stdout);
#endif
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int h, q;
cin >> n >> h >> m >> q;
rep(j, 1, n) {
int a, b, c, d;
cin >> a >> b >> c >> d;
up.push_back(a * m + b);
down.push_back(c * m + d);//模拟差分标记,一点的前缀和等于前面的正标记数减去负标记数
}
sort(up.begin(), up.end());
sort(down.begin(), down.end());
rep(j, 1, q) {
int a, b;
cin >> a >> b;
int pos = a * m + b;
int zheng = lower_bound(up.begin(), up.end(), pos)-up.begin();//正标记数
int fu = lower_bound(down.begin(), down.end(), pos)-down.begin();//负标记数
if (zheng != fu) {
cout << "No\n" ;
}
else {
cout << "Yes\n";
}
}
return 0;
}
可以看出,上面这种方法只适用于查询单点是否被标记。
还有一种方法是将有交集的区间合并,根据右端点排序,然后二分查找这个点是否在被标记区间内。