codevs大师题 1299 切水果 详细题解(标记线段树板子题)

本人第一篇博客,望支持,今后会持续更新codevs大师题。建议有一些基础(初步了解数据结构,基本数论、图论等)。

因为本人尚在学习,所以时间较少,题解较简略,见谅。 

其实这道题虽然是大师题,但是说实话和某些大师题 相比只能是水题。

题目描述 Description

简单的说,一共N个水果排成一排,切M次,每次切[L,R]区间的所有水果(可能有的水果被重复切),每切完一次输出剩下水果数量。

先分析一下题目,首先,N个水果排成一排,每次切[L,R]区间的所有水果,很容易看出是用线段树。所以这时候就毫不犹豫地码玩线段树板子就行了。

其次,因为可能有的水果被重复切,所以如何用线段树板子会导致区间内某一个被重复减去多次,从而造成答案错误。

所以需要在清除标记和更改点值的时候进行判断,if(tree[v].val<0)tree[v].val=0;  这样一句就可以完美解决。

另外,由于数据量大,所以还在用流出入输出的就不得不改了。scanf和printf是可以的,不过这里我要介绍一种快读方式,码量非常小:

int read(){
    int x=0;char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=gc();
    return x;
}

调用的时候直接用就好。

 

 

#include<bits/stdc++.h>
#define gc getchar
using namespace std;
const int M=1500005;
int n,m;
struct node{int l,r,tot,mid,val,tag;}tree[M];
void built(int x,int l,int r){
	tree[x].l=l;
	tree[x].r=r;
	tree[x].tot=r-l+1;
	if(l==r)tree[x].val=1;
	else{
		int mid=(l+r)/2;
		tree[x].mid=mid;
		built(x*2,l,mid);
		built(x*2+1,mid+1,r);
		tree[x].val=tree[2*x].val+tree[2*x+1].val;
	}
}
void addnote(int v,int z){
	tree[v].val+=z*tree[v].tot;
	if(tree[v].val<0)tree[v].val=0;
	tree[v].tag+=z;
}
void clear(int v){
	addnote(2*v,tree[v].tag);
	addnote(2*v+1,tree[v].tag);
	tree[v].tag=0;
}
void add(int v,int a,int b,int z){
	if(tree[v].l==a&&tree[v].r==b){addnote(v,z);return;}
	clear(v);
	if(b<=tree[v].mid) add(v*2,a,b,z);
	else if(a>tree[v].mid) add(v*2+1,a,b,z);
	else{
		add(2*v,a,tree[v].mid,z);
		add(2*v+1,tree[v].mid+1,b,z);
	}
	tree[v].val=tree[v*2].val+tree[v*2+1].val;
}
int find(int v,int a,int b){
	if (tree[v].l==a&&tree[v].r==b) return tree[v].val;clear(v);
	if (b<=tree[v].mid) return find(v*2,a,b);
	else if (a>tree[v].mid) return find(v*2+1,a,b);
	else return find(v*2,a,tree[v].mid)+find(v*2+1,tree[v].mid+1,b);
}
int read(){//快读 
    int x=0;char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=gc();
    return x;
}
int main(){
	int l,r;
	n=read();
	m=read();
	built(1,1,n);//建树 
	for(int i=1;i<=m;i++){
		l=read();
		r=read();
		if(l>r)swap(l,r);
		if(l<1)l=1;if(r>n)r=n;
		if(find(1,l,r)!=0)add(1,l,r,-1);
		printf("%d\n",find(1,1,n));
	}
	return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值