hdu4614 段设置

题目第一个坑就是下标是[0,n-1];

问题是:第一个插花的位置和最后一个插花的位置,还有拔了多少花

直接上代码,注释已经很全了

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 50010;
int n,m;
struct Node
{
    int l,r;
    int sum;//可以插花的位置
    int lazy;
    int first;//第一可以插花的位置
    int last;//最后插花的位置
}segTree[MAXN*3];//好像线段树这种题开到3倍就可以,没要4倍。
void pushup(int i)
{
    if(segTree[i].l==segTree[i].r)return;//到叶子节点
    segTree[i].sum = segTree[i<<1].sum+segTree[(i<<1)|1].sum;
    if(segTree[i<<1].first != -1)segTree[i].first = segTree[i<<1].first;
    else segTree[i].first = segTree[(i<<1)|1].first;
    if(segTree[(i<<1)|1].last != -1)segTree[i].last = segTree[(i<<1)|1].last;
    else segTree[i].last = segTree[(i<<1)].last;
}
void pushdown(int i)
{
    if(segTree[i].r == segTree[i].l)
        return;
    if(segTree[i].lazy == 1)
    {
        segTree[i<<1].first =segTree[i<<1].l;
        segTree[i<<1].last = segTree[i<<1].r;
        segTree[i<<1].sum = segTree[i << 1].r - segTree[i<<1].l+1;
        segTree[i<<1].lazy = 1;
        segTree[i<<1|1].first =segTree[i<<1|1].l;
        segTree[i<<1|1].last = segTree[i<<1|1].r;
        segTree[i<<1|1].sum = segTree[i << 1|1].r - segTree[i<<1|1].l+1;
        segTree[i<<1|1].lazy = 1;
    }

    if(segTree[i].lazy == -1)
    {
        segTree[i<<1].first = segTree[i<<1|1].first = -1;
        segTree[i<<1].last = segTree[i<<1|1].last = -1;
        segTree[i<<1].sum = segTree[i<<1|1].sum = 0;
        segTree[i<<1].lazy = segTree[i<<1|1].lazy = -1;
    }
    segTree[i].lazy = 0;
}
void build(int l,int r,int k)
{
    segTree[k].l = l;
    segTree[k].r = r;
    segTree[k].sum = r - l + 1;
    segTree[k].lazy = 0;
    segTree[k].first = l;
    segTree[k].last = r;
    if(l == r)
    {
        return;
    }
    int mid = (l + r) /2;
    build(l,mid,k*2);
    build(mid+1,r,k*2+1);

}
int query1(int i,int l,int r)
{
    if(segTree[i].l == l && segTree[i].r == r)
    {
        return segTree[i].first;
    }
    pushdown(i);
    int mid = (segTree[i].l + segTree[i].r)/2;
    int ans1;
    if(r <= mid)return query1(i<<1,l,r);
    else if(l > mid)return query1((i<<1)|1,l,r);
    else
    {
        ans1 = query1(i<<1,l,mid);
        if(ans1 != -1)return ans1;
        return query1((i<<1)|1,mid+1,r);
    }
}
int query2(int l,int r,int k)
{
    if(segTree[k].l == l && segTree[k].r == r)
    {
        return segTree[k].last;
    }
    pushdown(k);
    int ans ;
    int mid = (segTree[k].l+ segTree[k].r) /2;
    if(r <= mid)
        return query2(l,r,k*2);
    else if(l > mid)
        return query2(l,r,k*2+1);
    else
    {
        ans = query2(mid+1,r,k*2+1);
        if(ans != -1)
        {
            return ans;
        }
        else
            return query2(l,mid,k*2);
    }
}
int sum(int i,int l,int r)
{
    if(segTree[i].l == l && segTree[i].r == r)
    {
        return segTree[i].sum;
    }
    pushdown(i);
    int mid = (segTree[i].l + segTree[i].r)/2;
    if(r <= mid)return sum(i<<1,l,r);
    else if(l > mid)return sum((i<<1)|1,l,r);
    else return sum((i<<1)|1,mid+1,r)+sum(i<<1,l,mid);
}
void update(int i,int l,int r,int type)
{
    if(segTree[i].l == l && segTree[i].r==r)
    {
        if(type == 0)
        {
         
            if(segTree[i].sum == 0)//*
                这个地方要注意 当前范围没有位置可以插花了 就可以不用标记了
                是因为如果你标记了  后来如果是另一种操作就会改变此标记 然后传下去
                好像实质上是因为范围是空,它的first和last都是-1,也没有我下两行的必要了
            {
         
                segTree[i].first = -1;
                segTree[i].last = -1;
                return;
            }
            segTree[i].sum = 0;
            segTree[i].lazy = -1;
            segTree[i].first = -1;
            segTree[i].last = -1;
            return;
        }
        else if(type == 1)
        {
            if(segTree[i].sum == segTree[i].r-segTree[i].l+1)
            {
                segTree[i].first = segTree[i].l;
            segTree[i].last = segTree[i].r;
               return; 
            }
            segTree[i].sum = segTree[i].r-segTree[i].l+1;
            segTree[i].lazy = 1;
            segTree[i].first = segTree[i].l;
            segTree[i].last = segTree[i].r;
            return;
        }

    }
    pushdown(i);
    int mid = (segTree[i].l + segTree[i].r)/2;
    if(r <= mid)update(i<<1,l,r,type);
    else if(l > mid)update((i<<1)|1,l,r,type);
    else
    {
        update(i<<1,l,mid,type);
        update((i<<1)|1,mid+1,r,type);
    }
    pushup(i);
}
int bisearch(int A ,int F)
{
    if(sum(1,A,n) == 0)//如果没有花朵可以插入 那么返回
        return -1;
    if(sum(1,A,n) < F)//如果空余位置多了就可以返回最后一个插入的位置了
        return n;
    int l = A, r =n;
    int ans = A;
    while(l<=r)//此处的二分是这道题的精髓,不断二分查找最近右边界,最近!
    {
        int mid = (l+r)>>1;
        if(sum(1,A,mid) >= F)
        {
            ans = mid;
            r = mid -1 ;
        }
        else
        {
            l = mid + 1;
        }
    }
    return ans;
}
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        cin >> n >> m;
        build(1,n,1);
        int op,u,v;
        while(m--)
        {
            cin >> op >> u >> v;
            if(op==1)
            {
                u++;
                int t = bisearch(u,v);
                if(t == -1)
                {
                    cout << "Can not put any one." << endl;
                    continue;
                }
                printf("%d %d\n",query1(1,u,t)-1 ,query2(u,t,1)-1);
                update(1,u,t,0);
            }
            else
            {
                u++;
                v++;
                cout << v-u+1 - sum(1,u,v) <

 

你可能感兴趣的:(ACM,线段树)