有 个商品和 种颜色,第 个物品颜色为 ,价格为 。
对于一个购买物品的方案 ,定义其价格为 。
一个方案合法当且仅当:
- 对于第 种颜色,购买的该种颜色的商品的个数必须要在区间 中;
对于所有合法的方案,将其按照价格升序排序后,对于 ,求出第 种合法方案的价格(合法方案数不足 则输出 )。
,,,。
对于这种题,一种思路是考虑第 小和第 小的方案间的差异,不过这种思路在本题行不通。
还有一种思路是考虑设计一种转移顺序,使得每种方案都可以被简单表示,每种方案的后继方案只有 个,转移到每种方案的路径唯一,且转移路径上方案的代价单调。这其实相当于隐式建出一个包含所有方案的 DAG,满足:
- 每个点代表一种方案,且可以用 的信息量存储;
- 入度为 的点是最优方案 ;
- 每个点的出点都比它劣;
- 从 到每个点的路径有且仅有一条;
- 每个点的出点个数只有 个;
这样就可以用优先队列来 求解第 优的方案,即每次取出优先队列中的最优方案,将其所有后继方案加入优先队列。
Part 1:
先给 升序排序。注意到最优方案显然是选择前 个,次优方案则是将第 个选择的物品往后推一位(即用 替代 )。
观察到对于一段连续的选择的 ,将 替换为 ()显然不如将 替换为 ,所以得到一个方案的路径一定形如:
- 将第 个选择的物品往后推若干位,但不能越过第 个选择的物品(初始 );
- 令 ;
不难发现,第 个物品若没有往后推过,则第 个物品无法往后推,故可以让 减一的时候顺便将该物品往后推一位,这样就只用考虑当前物品往后推一位的情况,出点个数就对了。
则可以这样设计 DAG:
-
每个点为四元组 ,表示代价为 ,当前 ,第 个选择的物品是 ,第 个选择的物品是 ;
-
最优方案为 ;
-
点 的出点为:
-
(要求 );
-
(要求 );
-
Part 2:
当 , 且 时加入出点 即可。
Part 3:All
不妨将每种颜色看成黑盒,即设 表示颜色 的第 小方案的代价,那么状态可以看作一个长 的序列 ,其代价为 ,最优状态为 。不难发现可以这样设计转移使得路径唯一:
- 将 加一(不能超过颜色 的总方案数,初始 );
- 将 加一;
但是这样后继状态数太多了。
注意到,将颜色按照 升序排序即可避免这个问题,本质上是将一个点 的所有出点按照代价从小到大排序后连成一条链,这样 就只需要连代价最小的出点。
这样设计 DAG:
- 每个点为三元组 ,表示代价为 ,当前 ,;
- 特判掉最优方案,从次优方案 开始;
- 的出点为:
- (要求 存在);
- (要求 );
- (要求 且 );
最后一种出点相当于撤销了 的一次操作,即通过两次实现“跳过颜色 ”这个操作。由于按照 升序排序,故这样做是对的。
时间复杂度 ,实现起来要特判只有一种方案的颜色。
代码如下:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int S=200005,inf=1e8;
int n,m,k;
vector<ll> a[S];
int lb[S],rb[S];
priority_queue<pair<ll,pair<int,pair<int,int> > > > que[S];
vector<ll> vec[S];
vector<pair<ll,int> > idx;
priority_queue<pair<ll,pair<int,int> > > q;
inline void insnxt(int id,ll pre,int p,int i,int nxt)
{
int r=rb[id];
auto &c=a[id];
if(i>0&&i<c.size()&&i+1<nxt)
que[id].emplace(-(pre-c[i-1]+c[i]),make_pair(p,make_pair(i+1,nxt)));
if(p>0&&p+1<i)
que[id].emplace(-(pre-c[p-1]+c[p]),make_pair(p-1,make_pair(p+1,i)));
if(p==i-1&&i<r&&nxt==inf)
que[id].emplace(-(pre+c[i]),make_pair(i,make_pair(i+1,inf)));
}
inline void init(int id)
{
ll sm=0;
for(int i=0;i<lb[id];i++) sm+=a[id][i];
vec[id].push_back(sm);
insnxt(id,sm,lb[id]-1,lb[id],inf);
}
inline bool getnxt(int id)
{
if(que[id].empty()) return false;
auto t=que[id].top();
que[id].pop();
vec[id].push_back(-t.first);
insnxt(id,-t.first,t.second.first,t.second.second.first,t.second.second.second);
return true;
}
inline ll getrk(int id,int rk)
{
while(vec[id].size()<rk) if(!getnxt(id)) return -1;
return vec[id][rk-1];
}
inline void insnxt(ll pre,int i,int ct)
{
int id=idx[i].second;
if(getrk(id,ct+1)!=-1)
q.emplace(-(pre-getrk(id,ct)+getrk(id,ct+1)),make_pair(i,ct+1));
if(i+1<idx.size())
q.emplace(-(pre-getrk(idx[i+1].second,1)+getrk(idx[i+1].second,2)),make_pair(i+1,2));
if(ct==2&&i+1<idx.size())
q.emplace(-(pre-getrk(id,2)+getrk(id,1)-getrk(idx[i+1].second,1)+getrk(idx[i+1].second,2)),make_pair(i+1,2));
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
{
int x,y;
cin>>x>>y;
a[x].push_back(y);
}
for(int i=1;i<=m;i++) cin>>lb[i]>>rb[i];
for(int i=1;i<=m;i++) sort(a[i].begin(),a[i].end());
for(int i=1;i<=m;i++)
{
// printf("[%d %d]\n",lb[i],rb[i]);
// for(int x:a[i]) cout<<x<<' ';cout<<'\n';
rb[i]=min(rb[i],(int)a[i].size());
if(lb[i]>rb[i])
{
for(int i=1;i<=k;i++) cout<<"-1\n";
return 0;
}
}
ll sm=0;
for(int i=1;i<=m;i++)
{
init(i);
sm+=getrk(i,1);
if(getrk(i,2)!=-1) idx.emplace_back(getrk(i,2)-getrk(i,1),i);
}
// for(int i=1;i<=m;i++)
// {
// for(int j=1;j<=10;j++) cout<<getrk(i,j)<<' ';
// cout<<'\n';
// }
sort(idx.begin(),idx.end());
if(idx.size()>0)
{
int id=idx[0].second;
q.emplace(-(sm-getrk(id,1)+getrk(id,2)),make_pair(0,2));
}
cout<<sm<<'\n';
for(int i=1;i<=k-1;i++)
{
if(q.empty()) cout<<"-1\n";
else
{
auto t=q.top();
q.pop();
cout<<(-t.first)<<'\n';
insnxt(-t.first,t.second.first,t.second.second);
}
}
return 0;
}