前缀和差分练习

目录

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;
}

可以看出,上面这种方法只适用于查询单点是否被标记。 

还有一种方法是将有交集的区间合并,根据右端点排序,然后二分查找这个点是否在被标记区间内。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值