[P1311][NOIP2011]选择客栈

原题链接

不知道标准正解是啥
用了个有点奇葩的线段树
记录[l,r]这段区间内
第一个价格<=p的位置

有一个写起来异常简洁的思路
而且是O(n)复杂度
在这里贴一下链接
戳我(。・ω・。)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define maxn 200005
using namespace std;

int n,k,p,cl[maxn],mon[maxn],mr[4*maxn],tot[55],pos[55][maxn],ans;

void build(int id,int l,int r)
{
    if(l==r) 
    {
        if(mon[l]<=p) mr[id]=l;
        else mr[id]=maxn;
        return;
    }

    int mid=(l+r)>>1;

    build(id<<1,l,mid);
    build((id<<1)|1,mid+1,r);

    mr[id]=min(mr[id<<1],mr[(id<<1)|1]);        
}

int search(int id,int l,int r,int lef,int rig)
{
    int mid=(l+r)>>1;

    if(lef>r||rigreturn maxn;
    if(lef<=l&&rig>=r) return mr[id];

    if(rig<=mid) return search(id<<1,l,mid,lef,rig);    
    if(lef>mid) return search((id<<1)|1,mid+1,r,lef,rig);

    return min(search(id<<1,l,mid,lef,mid),search((id<<1)|1,mid+1,r,mid+1,rig));
}

int main()
{
    freopen("hotel.in","r",stdin);
    freopen("hotel.out","w",stdout);    

    int c,i,j,r,q;

    scanf("%d%d%d",&n,&k,&p); 

    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&cl[i],&mon[i]);
        c=cl[i];

        tot[c]++;
        pos[c][tot[c]]=i;
    }

    build(1,1,n);   

    for(i=0;ifor(j=1;jif(mon[pos[i][j]]<=p)
            {
                ans=ans+tot[i]-j;
                continue;
            }

            r=search(1,1,n,pos[i][j],n);//求出最近到哪一个位置才能<=p 
            if(r==maxn) continue;

            q=lower_bound(pos[i]+j+1,pos[i]+tot[i]+1,r)-pos[i];
            ans=ans+tot[i]-q+1;
        }

    printf("%d",ans);
    return 0;
}

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