[NOIP2017模拟]小店购物

2017.11.7 T3 2046

样例数据
输入

3 3
1 1
2 2
3 3
2 10
1 1 6 6
2 10

输出

10
9

分析:哎,T2耗时太久,本来可以打一下这道题的20%暴力的,说一下正解:
将所有的物品价值、价格和物品修改操作的都存下来,离散化按价值(从大到小)为第一关键字、价格(从小到大)为第二关键字排序,建一棵线段树(存价格),每一层的值都是下两层中较小的一个,这样在线段树上查找时只要左边的价格还能买就往左走(因为左边的价值更高),找到能买的物品。对于修改操作,显然不能在修改之前就买到这种价格的东西,所以就把价格赋值成INF,在枚举到这个修改后才把价格赋成修改后的,再把原价格赋成INF,也就没法买原来这种价格的东西了。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;isdigit(ch);ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int N=100005,INF=0x3f3f3f3f;
int n,m,cnt;
int b[N<<1],tree[N<<3];
struct node
{
    int k,w,p,id;
    inline friend bool operator <(const node &a,const node &b)
    {
        if(a.w==b.w)return a.preturn a.w>b.w;
    }
}a[N],t[N<<1],q[N];

void lsh()
{
    for(int i=1;i<=n;++i)
        t[++cnt]=a[i],t[cnt].id=i;
    for(int i=1;i<=m;++i)
        if(q[i].w!=-1)
            t[++cnt]=q[i],t[cnt].id=i;
}

void buildtree(int num,int l,int r)
{
    if(l==r)
    {
        tree[num]=t[l].p;
        return;
    }

    int mid=l+r>>1;
    buildtree(num<<1,l,mid);
    buildtree(num<<1|1,mid+1,r);
    tree[num]=min(tree[num<<1],tree[num<<1|1]);
}

void modify(int num,int l,int r,int p,int v)
{
    if(l==r)//找到位置后修改价格
    {
        t[l].p=tree[num]=v;
        return;
    }

    int mid=l+r>>1;
    if(p<=mid)
        modify(num<<1,l,mid,p,v);
    else
        modify(num<<1|1,mid+1,r,p,v);
    tree[num]=min(tree[num<<1],tree[num<<1|1]);
}

node query(int num,int l,int r,int v)
{
    if(l==r)//找到位置后返回能买的东西的所有信息(价格、价值)
        return t[l];

    int mid=l+r>>1;
    if(tree[num<<1]<=v)//只要左边能走就走左边
        return query(num<<1,l,mid,v);
    else
        return query(num<<1|1,mid+1,r,v);
}

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

    n=getint(),m=getint();
    for(int i=1;i<=n;++i)//物品存下来
        a[i].w=getint(),a[i].p=getint();
    for(int i=1;i<=m;++i)//修改询问也都存下来
    {
        int op=getint();
        if(op==1)
            q[i].k=getint(),q[i].w=getint(),q[i].p=getint();
        else
            q[i].k=getint(),q[i].w=-1;
    }

    lsh();//离散化
    sort(t+1,t+cnt+1);//按我说的关键字排序

    for(int i=1;i<=cnt;++i)
        if(!t[i].k)//是物品就把离散化后它的位置给它
            a[t[i].id].id=i;
        else//是修改操作也把离散化后它的位置给它,并把要建树的价格赋成INF
            q[t[i].id].id=i,t[i].p=INF;

    buildtree(1,1,cnt);

    for(int i=1;i<=m;++i)
    {
        if(q[i].w!=-1)//碰到修改操作
        {
            modify(1,1,cnt,a[q[i].k].id,INF);//把原来的价格赋成INF
            modify(1,1,cnt,q[i].id,q[i].p);//把新价格附进去
            a[q[i].k].id=q[i].id;//给物品新价值编号
        }
        else//碰到查询操作
        {
            long long ans=0;
            while(q[i].k>=tree[1])//只要还能买(tree[1]是整棵树里最小的价格)就买
            {
                node tmp=query(1,1,cnt,q[i].k);
                ans+=(long long)q[i].k/tmp.p*tmp.w;//可以买多次
                q[i].k%=tmp.p;//更新钱
            }
            cout<'\n';//输出答案
        }
    }
    return 0;
}

本题结。

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