本人第一篇博客,望支持,今后会持续更新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;
}