7.8 MHDU 4614Vases and Flowers

来源:互联网 发布:linux mv移动文件夹 编辑:程序博客网 时间:2024/05/24 01:20

题意:Alice有许多花要插到花瓶中,每个花瓶只能插一枝花,有以下几种操作:

1.清空目标区间内所有花瓶。

2.给目标区间插上x支花,从首个空的花瓶开始插花。

3.询问目标区间有多少支花。


思路:首先要用到区间赋值的线段树,清空花瓶的过程可以直接完美模拟,那么插花怎么办呢?这时要用到二分来寻找插花的起点和终点,对于能插完x支花和不能插完x支花的情况还要分类讨论。


代码:

#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn=50001,logn=20;int n,m;int setv[maxn*logn],sumv[maxn*logn],_qL,_qR,_Value,_sum;//维护信息void maintain(int o, int L, int R){    int lc = o*2, rc = o*2+1;    if(R > L){        sumv[o] = sumv[lc] + sumv[rc];    }    if(setv[o] >= 0)  sumv[o] = (R-L+1) * setv[o];}//标记传递void pushdown(int o){    int lc = o*2, rc = o*2+1;    if(setv[o] >= 0){//-1表示没有标记过节点        setv[lc] = setv[rc] = setv[o];        setv[o] = -1;//清除本节点的标记    }}void update(int o, int L, int R){    int lc = o*2, rc = o*2+1;    if(_qL <= L && R <= _qR) setv[o] = _Value;    else{        pushdown(o);        int M = (L + R) / 2;        if(_qL <= M) update(lc, L, M); else maintain(lc, L, M);        if(_qR > M) update(rc, M+1, R); else maintain(rc, M+1, R);    }    maintain(o, L, R);}void query(int o, int L, int R){    if(setv[o] >= 0){//递归边界1:有set标记        _sum += setv[o] * (min(R, _qR) - max(L, _qL) + 1);    }    else if(_qL <= L && R <= _qR){//递归边界2:边界区间,此区间没有收到set影响        _sum += sumv[o];    }    else{//递归统计        int M = (L + R) / 2;        if(_qL <= M) query(o*2, L, M);        if(_qR > M) query(o*2+1, M+1, R);    }}void Update(int l,int r,int v){    _qL=l; _qR=r; _Value=v;    update(1,1,n);}int Query(int l,int r){    _sum=0;    _qL=l; _qR=r; query(1,1,n);    return _sum;}void init(){    memset(sumv,0,sizeof(sumv));    memset(setv,-1,sizeof(setv));    setv[0]=1;}bool C1(int ept1,int l,int r){    int ept2=r-l+1-Query(l,r);    return ept2<ept1;}bool C2(int ept1,int l,int r){    int ept2=r-l+1-Query(l,r);    return ept2<ept1;}bool C3(int amt,int l,int r){    int ept=r-l+1-Query(l,r);    return ept>=amt;}bool C4(int amt,int l,int r){    int ept=r-l+1-Query(l,r);    return ept>=amt;}int main(){    int T;    scanf("%d",&T);    while(T--){        scanf("%d%d",&n,&m);        init();        for(int i=0;i<m;i++){            int op,l,amt,r;            scanf("%d%d",&op,&l);l++;            if(op==1){                scanf("%d",&amt);                int used=Query(l,n);int ept=n-l+1-used;                if(ept==0){                    printf("Can not put any one.\n");                    continue;                }                if(ept<amt){                    //二分找使得ept-1的下界和上界。                    int l1,r1,mid1,l2,r2,mid2;                    l1=l;  r1=n+1;                    while(r1-l1>1){                        mid1=(l1+r1)/2;                        if(C1(ept,l,mid1))l1=mid1;                        else r1=mid1;                    }                    if(r1>n||Query(r1,r1)==1) r1--;                    l2=l; r2=r1+1;                    while(r2-l2>1){                        mid2=(l2+r2)/2;                        if(C2(ept,mid2,r1))r2=mid2;                        else l2=mid2;                    }                    printf("%d %d\n",l2-1,r1-1);                    Update(l2,r1,1);                }                else{                    //二分找使得ept恰好等于amt的点,再找在ept等于amt时的上界。                    int l1,r1,mid1,l2,r2,mid2;                    l1=l-1; r1=n+1;                    while(r1-l1>1){                        mid1=(l1+r1)/2;                        if(C3(amt,l,mid1))r1=mid1;                        else l1=mid1;                    }                    if(r1>n||Query(r1,r1)==1) r1--;                    l2=l-1; r2=r1+1;                    while(r2-l2>1){                        mid2=(l2+r2)/2;                        if(C4(amt,mid2,r1)) l2=mid2;                        else r2=mid2;                    }                    printf("%d %d\n",l2-1,r1-1);                    Update(l2,r1,1);                }            }            else{                scanf("%d",&r);r++;                printf("%d\n",Query(l,r));                Update(l,r,0);            }        }        printf("\n");    }    return 0;}

0 0