【题解】[CCO2020] Shopping Plans
先考虑 \(60\) 分做法,每种颜色恰好选择一个。
那么最优值一定是全部选最小的,最大值一定是全部选最大的。
经典贪心模型,我们用 \(m\) 个指针表示每种颜色选到了第几个,每次从选择一个指针向后移动一格,同时用堆维护当前最小的状态,可以做到 \(\mathcal{O}(N+MK)\) 的时间复杂度,能够拿到 \(20\) 分。
现在考虑优化,观察到 \(m\) 个指针每次只移动一个指针,其他大部分指针大部分时间都是无用的。
考虑只记录当前指针 \(p\) ,每次可以选择将当前指针对应位置向后移动一格,也可以选择将指针向后移动若干格。
这样空间复杂度优化至 \(O(N+M+K)\) ,时间常数更小,可以得到 \(40\) 分。
由于指针 $p $ 只能向后移动,考虑每次只能将指针向后移动一格,而不是若干个。
这里我们利用撤销操作,如果当前指针只选了一格数,则可以选择撤销,并将指针向后移动一格。
这样时间复杂度 \(\mathcal{O}(K\log K+N+M)\) ,可以得到 \(60\) 分。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
using namespace std;
typedef long long ll;
struct node{
int x,y;ll val;
node(int X=0,int Y=0,ll V=0){x=X,y=Y,val=V;}
bool operator<(const node o)const{return val>o.val;}
};
int n,m,k,p[N];ll ans;priority_queue<node>q;
vector<int>a[N];
bool cmp(int x,int y){
if(a[x].size()<=1)return 0;
if(a[y].size()<=1)return 1;
return a[x][1]-a[x][0]<a[y][1]-a[y][0];
}
inline void ext(){while(k--)puts("-1");}
int main(){
scanf("%d%d%d",&n,&m,&k);
rep(i,1,n){
int x,y;scanf("%d%d",&x,&y);
a[x].push_back(y);
}
rep(i,1,m){
if(!a[i].size()){ext();return 0;}
sort(a[i].begin(),a[i].end());
ans+=a[i][0];p[i]=i;
}
sort(p+1,p+m+1,cmp);
//cout<<"uu "<<endl;
if(a[p[1]].size()<2){ext();return 0;}
printf("%lld\n",ans);k--;
q.push(node(1,2,a[p[1]][1]-a[p[1]][0]));
while(k&&!q.empty()){
k--;node cur=q.top();q.pop();
printf("%lld\n",cur.val+ans);
int x=p[cur.x],y=p[cur.x+1];
if(cur.y<(int)a[x].size())q.push(node(cur.x,cur.y+1,cur.val+a[x][cur.y]-a[x][cur.y-1]));
if(a[y].size()>1){
q.push(node(cur.x+1,2,cur.val+a[y][1]-a[y][0]));
if(cur.y==2)q.push(node(cur.x+1,2,cur.val+a[y][1]-a[y][0]-a[x][1]+a[x][0]));
}
}
ext();return 0;
}
我们可以延续 \(x=y=1\) 的做法,现在每种颜色可以选多个数。
对于当前颜色,我们可以把一种方案看成 \(x=y=1\) 时的一个物品,现在我们只需要对于每种颜色,快速求出第 \(k\) 小的方案。
这是与前面不相干的子问题,我们可以分开解决。
如果我们钦定选择 \(s\) 个数,那么最小的方案一定是选择最小的 \(s\) 个数,最大的方案一定是选择最大 \(s\) 个数,其余的任何方案都可以表示为从最小的 \(s\) 个数开始,选择若干个数向后移动若干位。
其中移动时两个数不能相交,一个数不能越过另一个数。
那么我们维护四元组 \((i,j,k,val)\) ,分别表示当前需要移动的是第 \(j\) 个数,它前面是第 \(i\) 个数,它后面是第 \(k\) 个数,当前代价是 \(val\) 。
对于任意一个 \(s\) 初始化插入 \((s-1,s,size+1,0)\) 。
转移,\((i,j,k,val)\to (i,j+1,k,a_{j+1}-a_j+val)\) , \((i,j,k,val)\to (i-1,i+1,j,a_{i+1}-a_i+val)\) 。
所以总状态数 \(<3k\) ,堆维护,时间复杂度 $\mathcal{O}((k+n)\log k) $ 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
#define inf 0x3f3f3f3f3f3f3f3fLL
#define int long long
using namespace std;
int n,m,k,ans,p[N];
struct color{
vector<int>c,w;
int l,r;
struct node{
int p,q,g,val;
node(int P=0,int Q=0,int G=0,int V=0){p=P,q=Q,g=G,val=V;}
bool operator<(const node o)const{return val>o.val;}
};
priority_queue<node>q;
bool init(){
if(l>(int)c.size())return true;
sort(c.begin(),c.end());
r=min(r,(int)c.size());
if(!r)w.push_back(0);
else{
if(l==0)w.push_back(0);
int sum = 0;
rep(i,0,l-1)ans += c[i];
rep(i,l,r-1){
if(i)q.push(node(i-1,i,c.size()+1,sum));
sum+=c[i];
}
q.push(node(r-1,r,c.size()+1,sum));
}return false;
}
void calc(){
if(q.empty()){w.push_back(inf);return;}
node cur=q.top();q.pop();
w.push_back(cur.val);
if(cur.q+1<cur.g)q.push(node(cur.p,cur.q+1,cur.g,cur.val+c[cur.q]-c[cur.q-1]));
if(cur.p>0&&cur.p+1<cur.q)q.push(node(cur.p-1,cur.p+1,cur.q,cur.val+c[cur.p]-c[cur.p-1]));
}
int get(int rk){
while(rk>(int)w.size())calc();
return w[rk-1];
}
}a[N];
inline void ext(){while(k--)puts("-1");}
bool cmp(int x,int y){return a[x].get(2)-a[x].get(1)<a[y].get(2)-a[y].get(1);}
struct node{
int x,y,val;
node(int X=0,int Y=0,int V=0){x=X,y=Y,val=V;}
bool operator<(const node o)const{return val>o.val;}
};
priority_queue<node>q;
signed main(){
scanf("%lld%lld%lld",&n,&m,&k);
rep(i,1,n){
int x,y;scanf("%lld%lld",&x,&y);
a[x].c.push_back(y);
}
rep(i,1,m)scanf("%lld%lld",&a[i].l,&a[i].r);
rep(i,1,m)if(a[i].init()){ext();return 0;}else p[i]=i;
sort(p+1,p+m+1,cmp);
printf("%lld\n",ans);k--;
q.push(node(1,2,a[p[1]].get(2)-a[p[1]].get(1)));
while(k&&!q.empty()){
node cur=q.top();q.pop();
if(cur.val<inf)printf("%lld\n",cur.val+ans);
else break;k--;
int x=p[cur.x],y=p[cur.x+1];
q.push(node(cur.x,cur.y+1,cur.val+a[x].get(cur.y+1)-a[x].get(cur.y)));
if(cur.x<m){
q.push(node(cur.x+1,2,cur.val+a[y].get(2)-a[y].get(1)));
if(cur.y==2)q.push(node(cur.x+1,2,cur.val+a[y].get(2)-a[y].get(1)-a[x].get(2)+a[x].get(1)));
}
}
ext();return 0;
}