P1204 [USACO1.2]Milking Cows 一维差分

本文介绍了差分数组的概念及其与前缀和的关系,通过实例解释了如何使用差分数组来标记区间并进行区间修改。在USACO1.2的挤牛奶问题中,通过差分数组找到最长连续工作和非工作时间。文章强调了差分数组在区间操作中的应用,并提供了一个C++代码示例来解决该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P1204 [USACO1.2]挤牛奶Milking Cows - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

        差分在上学期和前缀和一起学了,不过一直没用到过,就忘了,倒是前缀和cf用的很多。

设 sum为前缀和数组,由前缀和

sum[i] = sum[i-1]+arr[i]

[l,r]的和为,sum[r] - sum[l-1]

示意: 

而对前缀和公式移项:

sum[i] = sum[i-1]+arr[i]  -->   arr[i] = sum[i]-sum[i-1]

数学意义就是sn-sn-1=an

此时arr被称为差分数组

可以看出差分和前缀和其实是互逆得到操作。

------------------------------------------------

差分一般被用来标记区间。

如果要使[l,r]区间上每个元素a[i] 都加上C,

可以先得到a的差分数组b

因为a是b的前缀和,所以满足

a[i]=sum(b[1],b[2]....b[i])

如果使b[i]=b[i]+C

那么a数组中i之前的元素都没有变化,i之后的元素全部+C

这个很简单,如果使b[i]=b[i]+C,那么a[1],a[2]...a[i-1]都跟b[i]没关系,所以不改变

但是a[i],a[i+1],a[n] 因为都加上了b[i]所以值全部+C

而我们需要加C的区间为[l,r],所以令b[l]=b[l]+C,同时还需要让b[r+1]=b[r+1]-C。

看图就很好理解为什么b[r+1]需要-C了

b[l] + c,效果使得a数组中 a[l]及以后的数都加上了c(红色部分),但我们只要求l到r区间加上c, 因此还需要执行 b[r+1] - c,让a数组中a[r+1]及往后的区间再减去c(绿色部分),这样对于a[r] 以后区间的数相当于没有发生改变。 

所以差分数组标记完区间后,对其求前缀和即可得到区间修改后的数组a。


回到本题

 我们可以对有人工作的时间标记为1,无人工作的时间标记为0.最后统计整个区间内最长的连续1和最长的连续0.

而这个标记用的就是差分。

我们先初始化整个区间没人工作,全为0.然后在输入每个人工作时间的时候对工作时间的区间进行标记,有工作就+=1,没有就不加默认为0.

但是这题还有注意一点,设dif为差分数组。标记过程本来应该是

dif[l]+=1;
dif[r+1]-=1;

 但是这题的工作时间为左开右闭的,所以写成

dif[l]++;
dif[r]--;
#include <bits/stdc++.h>
#include<iostream>
#include<thread>
using namespace std;
#pragma warning(disable:4996);
#define ll long long
#define	int ll
#define endl "\n"
#define rep(j,b,e) for(int j=b;j<=e;j++)
int T;
const int N = 2e5 + 10;
int n, k, q;
void inline  inln() {}
template<class T, class ...Arg>
void inline  inln(T& t, Arg&...args) {
	cin >> t;
	inln(args...);
}
template<typename T>
void inline inln(vector<T>& arr, int n, int begin = 1) {
	if (arr.size() >= n + 1) {
		for (int j = begin; j <= n - 1 + begin; j++)
			cin >> arr[j];
		return;
	}
	int t;
	if (begin == 1) {
		arr.push_back(0);
	}
	for (int j = 1; j <= n; j++) {
		cin >> t;
		arr.push_back(t);
	}
}
void inline pln() {
	cout << "\n";
}
template <class T, class ...Arg>
void inline pln(T t, Arg ...args) {
	cout << t << " ";
	pln(args...);
}
template<typename T>
void inline pln(vector<T>& a, string s = " ") {
	for (T& x : a) {
		cout << x << s;
	}
	cout << endl;
}
signed main()
{
#ifndef ONLINE_JUDGE
	//freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(0); cout.tie(0);
	cin >> n;
	using node = pair<int, int>;
	vector<node>arr;
	vector<int>dif(1e6 + 10);
	int mx = 0;
	int mn = INT_MAX;
	rep(j, 1, n) {
		int a, b;
		inln(a, b);
		mx = max(mx, b);
		mn = min(mn, a);
		dif[a]++;//差分
		dif[b]--;
		arr.push_back({ a,b });

	}
	int ans1 = 0, ans2 = 0;
	int s1 = 0, s2 = 0;
	rep(j, mn, mx - 1) {
		if (j == 0) {
			dif[j] = 1;
		}
		else
			dif[j] += dif[j - 1];
		int ok = 1;
		if (dif[j] != 0) {
			s1++;

			s2 = 0;
		}
		else {
			s2++;
			s1 = 0;
		}
		ans2 = max(ans2, s2);
		ans1 = max(ans1, s1);
	}
	pln(max(s1, ans1), max(s2, ans2));
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值