hdu4614——线段树+二分

题目链接:https://vjudge.net/problem/HDU-4614#author=2013551629

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 3

Sample 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;
}
发布了342 篇原创文章 · 获赞 220 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43472263/article/details/103598392
今日推荐