Description
Alice有N个花瓶(标号为0~ N-1)。当她收到一些花时,她会随机的选择一个瓶子A,从它开始遍历A,A+1, A+2, ..., N-1号瓶子,遇到空瓶子就放一朵花进去,直到花朵放完或没有瓶子,剩下的花将被丢弃。有时,她也会清理标号从A到B的花瓶(A <= B).花瓶里的花会被丢弃。
Input
第一行一个整数T,表示数据组数。
每组数据,第一行一个整数N(1 < N < 50001) and M(1 < M < 50001). N 是花瓶个数, M 是Alice的操作次数. 接下来M行 行3个 整数. 第一个整数 K(1 or 2). 如果K=1, 后面跟两个整数 A 和 F . 表示Alice 得到了F 朵花并且把它们放入从A 的花瓶里. 如果K= 2, 后跟两个整数 A 和 B. 表示Alice 清理的花瓶标号范围(A <= B).Output
对于每个K=1的操作,输出第一朵和最后一朵花放置的花瓶标号。如果没有任何放花的位置,输出'Can not put any one.'.对于K=2的操作,输出丢弃花的个数.
每组数据后输出一个空行.Sample Input
2 10 5 1 3 5 2 4 5 1 1 8 2 3 6 1 8 8 10 6 1 2 5 2 3 4 1 0 8 2 2 5 1 4 4 1 2 3Sample Output
3 7 2 1 9 4 Can not put any one. 2 6 2 0 9 4 4 5 2 3题解
很不错的一道线段树+二分的题目,
首先我们可以用线段树维护一段区间内的空瓶子个数,同时用一个懒标记记录区间内是否所有的瓶子都为空。
操作2很简单,就是一个简单的区间查询,首先查询出[x,y]区间内的空瓶子个数,然后用总的区间长度减去空瓶子个数就是丢弃的花的个数,然后我们再把[x,y]区间的空瓶子都变成非空的,一个简单的区间修改。
对于操作1而言,我们首先查询出区间[x,n]区间的空瓶子个数,如果为0那么直接输出,否则我们需要二分去求出[x,n]区间内的第一个空瓶子位置和最后一个空瓶子位置,不过我们要先知道为什么能二分,因为[x,n]区间内的空花瓶数是单调不递减的,这个是很显然的,大区间的空瓶子数肯定是大于被包含的小区间的空瓶子个数。然后我们就可以在[x,n]区间去二分枚举空瓶子数等于1的位置,以及空瓶子数为min(花朵的数量,[x,n]区间内的空瓶子个数)的位置,再把这两个位置之间的空瓶都处理成非空的就行了,很经典的一道题。
代码实现
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define e 2.718281828 #define rp(i,s,t) for (i = (s); i <= (t); i++) #define RP(i,s,t) for (i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int N = 5e4+7; int n; struct node{ int l,r; int sum;//维护空瓶子的数量 int lazy;//标记区间内是否花瓶全为空,全空为0,否则为1 }tree[N<<2]; void pushup(int rt){ tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum; } void pushdown(int rt){ tree[rt<<1].lazy=tree[rt<<1|1].lazy=tree[rt].lazy; tree[rt<<1].sum=tree[rt].lazy*(tree[rt<<1].r-tree[rt<<1].l+1); tree[rt<<1|1].sum=tree[rt].lazy*(tree[rt<<1|1].r-tree[rt<<1|1].l+1); tree[rt].lazy=-1; } void build(int l,int r,int rt){ tree[rt].l=l,tree[rt].r=r; tree[rt].lazy=-1; tree[rt].sum=r-l+1; if(l==r) return; int m=(l+r)>>1; build(lson); build(rson); pushup(rt); } void update(int rt,int L,int R,int val){ if(L<=tree[rt].l&&tree[rt].r<=R){ tree[rt].lazy=val; tree[rt].sum=val*(tree[rt].r-tree[rt].l+1); return ; } if(tree[rt].l==tree[rt].r) return ; if(tree[rt].lazy!=-1) pushdown(rt); int m=(tree[rt].l+tree[rt].r)>>1; if(L<=m) update(rt<<1,L,R,val); if(m<R) update(rt<<1|1,L,R,val); pushup(rt); } int query(int rt,int L,int R){//查找[L,R]区间内的空瓶子个数 if(tree[rt].l>R||tree[rt].r<L) return 0; int ans=0; if(L<=tree[rt].l&&tree[rt].r<=R) return tree[rt].sum; if(tree[rt].lazy!=-1) pushdown(rt); int m=(tree[rt].l+tree[rt].r)>>1; if(L<=m) ans+=query(rt<<1,L,R); if(m<R) ans+=query(rt<<1|1,L,R); pushup(rt); return ans; } int bin_search(int x,int num){//二分查找第num个空瓶子的位置 int l=x,r=n; int ans=0,m=0; while(l<=r){ m=(l+r)>>1; if(query(1,x,m)>=num){//关键部分,精髓 ans=m; r=m-1; } else l=m+1; } return ans; } int main(){ int T=read(); while(T--){ n=read(); int m=read(); build(1,n,1); // printf("%d %d %d\n",tree[1].l,tree[1].r,tree[1].sum); while(m--){ int opt=read(),x=read(),y=read(); if(opt==1){ x++; int cnt=query(1,x,n);//得到[x,n]区间内的空花瓶个数 // cout<<cnt<<endl; if(cnt==0) printf("Can not put any one.\n");//没有空瓶的情况 else{ int l=bin_search(x,1);//二分得到第一个出现空瓶子的位置 int r=bin_search(x,min(y,cnt));//二分得到符合条件的最后一个出现空瓶子的位置 // cout<<l<<" "<<r<<endl; update(1,l,r,0);//将[l,r]区间内的空瓶子全部插上花 printf("%d %d\n",l-1,r-1); } } else{ x++,y++; printf("%d\n",y-x+1-query(1,x,y)); update(1,x,y,1);//将[l,r]区间内的瓶子都清空 } } printf("\n"); } return 0; }
hdu4614——线段树+二分
最新推荐文章于 2022-04-25 11:32:41 发布