Loading

【题解】[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;
}
posted @ 2021-06-08 10:28  7KByte  阅读(212)  评论(1编辑  收藏  举报