【USACO 2017 December Silver】Milk Measurement题解

Description
最初,农夫约翰的每头奶牛每天生产G加仑的牛奶(1≤G≤10^9)。由于随着时间的推移,奶牛的产奶量可能会发生变化,农夫约翰决定定期对奶牛的产奶量进行测量,并将其记录在日志中。
他的日志中的记录如下:
35 1234 -2
14 2345 +3
第一个条目表明:在第35天,1234号奶牛的产奶量比上次测量时降低了2加仑。
第二个条目表明:在第14天,2345号奶牛的产奶量比上次测量时增加了3加仑。
农夫约翰只有在任何一天内做最多一次测量的时间(即每天最多做一次测量,但可能不做)。不幸的是,约翰有点杂乱无章,他不一定按照时间顺序记下测量结果。为了保持奶牛的产奶动力,农夫约翰自豪地在谷仓的墙上展示了目前产奶量最高的奶牛的照片(如果有若干头奶牛的产奶量最高,他就会展示所有的图片)。

请求出约翰需要调整所展示的照片的次数。

请注意,农夫约翰有一大群奶牛。所以尽管日志中记录了一些奶牛改变了产奶量,但仍然还有很多奶牛的产奶量保持在G加仑。
Input
第一行是两个整数N和G,分别表示测量的次数和初始产奶量。
接下来N行,每行为一次测量。每行三个数:分别表示日期一(在整数1…106范围内),奶牛的编号(在整数1…109范围内),该奶牛的产奶量变化值(一个非负数)。无论如何,每头奶牛的产奶量永远保证在0…10^9范围内。
Output
请输出约翰总共调整所展示的照片的次数。
Sample Input
4 10
7 3 +3
4 2 -1
9 3 -1
1 1 +2
Sample Output
3
Solution
很明显是一道数据结构题,用各种数据结构维护一些值,然后分各种情况讨论。但是我们发现,奶牛的数量有10^9只,所以我们要优化。我们发现,会用到的奶牛最多只有1000000只,所以我们可以用map来离散化,这样就可以压缩空间。
用线段树维护后,我们分几种情况讨论,满足这几种情况的话答案++:

有5个值,分别是:max修改之前的最大值,max2修改前的第二大值,maxs修改前最大值的数量,pre我们当前要修改的奶牛原本的值,now这头奶牛修改后的值。
那么就有几种情况:
1、pre=MAX
2、preMAX&&nw1
3、pre
MAX&&nw<=MAX2&&MAXS1
4、pre
MAX&&nw>MAX&&MAXS>1

不难理解,自己慢慢领悟。

#include
#include
#include
using namespace std;
const int N=1e5+5;
int n,m,cnt=0,pre,nw,MAX,MAX2,MAXS,ans=0,tree[N<<2][3];
int read() {
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
		s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s*f;
}
struct node {
	int day,id,value;
}a[N];
map <int,int> hash;
bool cmp(node x,node y) {
	return x.day<y.day;
}
void update(int now,int l,int r,int x,int y) {
	if(l>x)return;
	if(r<x)return;
	if(l==r) {
		pre=tree[now][0];
		nw=tree[now][0]+y;
		tree[now][0]+=y;
		tree[now][2]=1;
		return;
	}
	int mid=(l+r)>>1;
	update(now<<1,l,mid,x,y);
	update((now<<1)|1,mid+1,r,x,y);
	tree[now][0]=max(tree[now<<1][0],tree[(now<<1)|1][0]);
	if(tree[now<<1][0]!=tree[(now<<1)|1][0]) {
		tree[now][1]=max(min(tree[now<<1][0],tree[(now<<1)|1][0]),max(tree[now<<1][1],tree[(now<<1)|1][1]));
		if(tree[now<<1][0]>tree[(now<<1)|1][0])tree[now][2]=tree[now<<1][2];
		else tree[now][2]=tree[(now<<1)|1][2];
	}
	else tree[now][1]=max(tree[now<<1][1],tree[(now<<1)|1][1]),tree[now][2]=tree[now<<1][2]+tree[(now<<1)|1][2];
}
void build(int now,int l,int r) {
	if(l==r) {
		tree[now][2]=1;
		return;
	}
	int mid=(l+r)>>1;
	build(now<<1,l,mid);
	build((now<<1)|1,mid+1,r);
	tree[now][2]=tree[now<<1][2]+tree[(now<<1)|1][2];
}
int main() {
	freopen("measurement.in","r",stdin);
	freopen("measurement.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++) {
		a[i].day=read();
		a[i].id=read();
		a[i].value=read();
	}
	sort(a+1,a+1+n,cmp);//要排序!! 
	for(int i=1;i<=n;i++)
		if(!hash[a[i].id])hash[a[i].id]=++cnt;
	build(1,1,cnt);
	for(int i=1;i<=n;i++) {
		MAX=tree[1][0];MAX2=tree[1][1];MAXS=tree[1][2];
		update(1,1,cnt,hash[a[i].id],a[i].value);
		if(i==1) {
			ans++;
			continue;
		}
		if(pre<MAX&&nw>=MAX)ans++;
		if(pre==MAX&&nw<MAX&&MAXS>1)ans++;
		if(pre==MAX&&nw<=MAX2&&MAXS==1)ans++;
		if(pre==MAX&&nw>MAX&&MAXS>1)ans++;
	}
	printf("%d",ans);
}

你可能感兴趣的:(线段树,USACO,分类讨论)