【题解】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;
}

浙公网安备 33010602011771号