【题解】P6646 [CCO 2020] Shopping Plans

P6646 [CCO 2020] Shopping Plans

题意

\(N\) 个商品,每个商品有个种类 \(a_i\),有个价格 \(c_i\)

对于第 \(j\) 个种类,必须购买个数位于 \([x_j,y_j]\) 的商品,即最少购买 \(x_j\) 个,最多购买 \(y_j\) 个该种类的商品。

您需要求出前 \(k\) 种便宜的方案所需的价钱,如果没有这样的方案,请输出 -1

特别的,如果有相同钱数,但是具体方案不相同的,算作两种方案。

题解

知识点:优先队列。

很牛又比较经典的题,前 \(k\) 大问题终极版。

这种题的经典套路就是用堆每次取出价值最小的节点,然后再用这个节点拓展出新的节点插入堆,为了正确性,拓展出的节点价值应该单调不增

纵观题目结构,这题的大概做法是对于每类物品,维护其前 \(k\) 小,再用每个类别的物品的前 \(k\) 小,去维护全局的前 \(k\) 小。

对于每一类物品,将其所有物品按价值从小到大排序,取出 \(x\) 个物品且和最小的情况肯定是一段前缀,那么就用这个性质表达节点的状态。

这里定义排序后第 \(i\) 个物品的价值为 \(w_i\),特殊地,\(w_0=0\)

定义四元组 \((v,a,b,c)\) 为其节点,\(v\) 表示当前的价值和,\(a\) 表示前缀 \(1\sim a\) 都还没有动,\(b\) 表示前缀后的第一个选定的物品位置,\(c\) 表示 \(b\) 物品后紧跟的下一个物品的位置,每次从堆中取出 \(v\) 最小的进行拓展:

  • \((v,a,b,c)\to (v-w_a+w_{a+1},a-1,a+1,b)\),相当于把连续前缀的最后一个选定物品“移动一格”,此时 \(a+1\) 变为前缀后的第一个选定的物品位置,而 \(b\) 就是紧跟的下一个。该转移需要保证 \(a+1<b,a-1\ge 0\)

  • \((v,a,b,c)\to (v-w_b+w_{b+1},a,b+1,c)\),相当于把 \(b\) 这个选定的物品移动了一格,转移要保证 \(b+1<c\)

当一个物品变到了 \(c\) 的位置,就不会再去动它了,这是保证拓展时间复杂度的必要,同时也能不重不漏覆盖所有情况。

对于一类物品,设选定物品上下界分别为 \(x,y\),初始时只需要插入 \(\displaystyle (\sum_{i=0}^p w_i,p-1,p,siz+1)\),其中 \(p\) 满足 \(p\in [x,\min(y,siz)]\)\(siz\) 是这类物品的个数。

考虑完了每类物品的内部情况,现在考虑外部计算总价值前 \(k\) 大的部分。

发现不存在第二小方案的物品可以直接计入答案,下面讨论的物品全都是至少有第二小方案的。

设总物品类数为 \(cnt\),设 \(w_{a,b}\) 为排序后第 \(a\) 类物品第 \(b\) 小的方案的价值。

定义三元组 \((v,a,b)\),表示处理到了 \(a\),使用了 \(a\) 类物品中的第 \(b\) 小方案,总价值为 \(v\),且 \(a+1\sim cnt\) 全都选第一小的方案,存在以下拓展:

  • \((v,a,b)\to (v-w_{a,b}+w_{a,b+1},a,b+1)\),转移要求 \(a\) 类物品存在第 \(b+1\) 小方案。

  • \((v,a,b)\to (v-w_{a+1,1}+w_{a+1,2},a+1,2)\),条件是 \(a+1\le cnt\)

  • 发现上面向 \(a+1\) 拓展时默认选择第 \(2\) 小方案拓展,所以还需要反悔操作来继续保持选择第 \(1\) 小方案,此时有拓展 \((val,a,2)\to (val+(w_{a,1}-w_{a,2})-(w_{a+1,1}-w_{a+1,2}))\),需要保证 \(a\neq 1,a+1\le cnt\)

设计地非常精妙,不重不漏,但我们需要对拓展的物品顺序手动排序,保证拓展后价值单调不增,即按 \(w_{a,1}-w_{a,2}\) 从大到小排序。

初始时插入 \((sum,1,1)\) 即可,\(sum\) 表示每一类物品第一大之和。

需要注意的细节是无解的判断,以及 \(x_i,y_i\) 可能等于 \(0\),坑死我了。

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()

#define N 202507
#define int long long

int n,m,k;
vector<int>e[N];

#define node pair<int,array<int,3>>
#define val(x) (x).fi
#define ga(x) (x).se[0]
#define gb(x) (x).se[1]
#define gc(x) (x).se[2]

struct ds{
    priority_queue<node,vector<node>,greater<node>>q;
    int now=0,id;
    vector<int>mem;

    inline int get(int rk){
        while(now<rk){
            if(q.empty()){
                return -1;
            }

            node u=q.top();
            q.pop();

            int v=val(u),a=ga(u),b=gb(u),c=gc(u);

            mem.pb(v);

            if(a-1>=0&&a+1<b){
                q.push({v-e[id][a]+e[id][a+1],{a-1,a+1,b}});
            }
            if(b&&b+1<c){
                q.push({v-e[id][b]+e[id][b+1],{a,b+1,c}});
            }

            now++;
        }

        return mem[rk-1];
    }    
}w[N];

int p[N],cnt;

#define arr array<int,3>

priority_queue<arr,vector<arr>,greater<arr>>q;

inline int qry(){
    if(q.empty()){
        return -1;
    }

    auto u=q.top();
    q.pop();

    int v=u[0],a=u[1],b=u[2];

    if(w[p[a]].get(b+1)!=-1){
        q.push({v-w[p[a]].get(b)+w[p[a]].get(b+1),a,b+1});
    }

    if(a+1<=cnt&&w[p[a+1]].get(2)!=-1){
        q.push({v-w[p[a+1]].get(1)+w[p[a+1]].get(2),a+1,2});
    }

    if(a!=1&&a+1<=cnt&&b==2&&w[p[a+1]].get(2)!=-1){
        q.push({v+(w[p[a]].get(1)-w[p[a]].get(2))-(w[p[a+1]].get(1)-w[p[a+1]].get(2)),a+1,2});
    }

    return v;
}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    cin>>n>>m>>k;

    rep(i,1,n){
        int a,c;
        cin>>a>>c;

        e[a].pb(c);
    }

    int add=0;

    rep(i,1,m){
        w[i].id=i;

        int x,y;
        cin>>x>>y;

        int len=sz(e[i]);
        e[i].pb(0);
        sort(all(e[i]));

        int tmp=0;
        rep(j,0,len){
            tmp+=e[i][j];

            if(j>=x&&j<=y){
                w[i].q.push({tmp,{j-1,j,len+1}});
                // cout<<i<<' '<<tmp<<"sb\n";
            }
        }
    }

    rep(i,1,m){
        if(w[i].get(1)==-1){
            rep(j,1,k){
                cout<<"-1\n";
            }
            return 0;
        }

        if(w[i].get(2)!=-1){
            p[++cnt]=i;
        }
        else{
            add+=w[i].get(1);
        }
    }

    if(!cnt){
        cout<<add<<"\n";
        rep(i,2,k){
            cout<<"-1\n";
        }

        return 0;
    }

    sort(p+1,p+1+cnt,[](int x,int y){
        return w[x].get(1)-w[x].get(2)>w[y].get(1)-w[y].get(2);
    });

    int sum=0;

    rep(i,1,cnt){
        sum+=w[p[i]].get(1);
    }

    q.push({sum,1,1});

    rep(i,1,k){
        int res=qry();

        if(res==-1){
            cout<<"-1\n";
        }
        else{
            cout<<res+add<<"\n";
        }
    }

    return 0;
}
posted @ 2025-07-21 16:03  Lucyna_Kushinada  阅读(12)  评论(0)    收藏  举报