把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P6831 [IOI2020]嘉年华奖券

题面传送门
首先看到题面中保证\(n\)为偶数,所以可以从这一个方面来推。
稍微用初一上的内容推一下就可以发现:对于一次取得升序序列\(a\),其权值为\(\sum\limits_{i=\frac{n}{2}+1}^{n}{a_i}-\sum\limits_{i=1}^{\frac{n}{2}}{a_i}\)
那么一定是在满足每种颜色\(k\)个的颜色下,最大的\(\frac{nk}{2}\)个为前面,其余为后面。
具体的,我们首先假设所有的数都是负的,然后考虑将一个数转化为正的,那么最优就是转化为所处颜色最大值。因为这个有单调性,所以可以用堆维护。
这样就可以求出答案。
接下来考虑怎么构造。
有一个显然的结论:前面的任何一个数都不小于后面的任何一个数,因为如果有一个小于的,那么就可以两边互换,就不是最优。
有了这个结论,就可以任意分组,但是还有颜色的限制。
这个限制考虑仍然用堆维护,找到含有前面元素个数最多的颜色,最先使用,其所对应的也是后面颜色最少的,这样能保证一定能匹配。
时间复杂度\(O(nmlogm)\)
代码实现:

#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
int a[1539][1539],n,m,k,h[1539],flag[1539];
struct ques{
	int w,id;
	bool operator <(const ques &x)const{return w<x.w;}
}w[1539];
std::priority_queue<ques> q;
struct yyy{int x,y;};
extern "C" void allocate_tickets(std::vector<std::vector<int> > s);
extern "C" long long find_maximum(int k,std::vector<std::vector<int> > x){
    long long ans=0;register int i,j;register ques tmp;register yyy now;
	n=x.size();m=x[0].size();
	for(i=0;i<n;i++){
		for(j=0;j<m;j++) a[i+1][j+1]=x[i][j];
	}
	for(i=1;i<=n;i++) sort(a[i]+1,a[i]+m+1);
	for(i=1;i<=n;i++)h[i]=k,q.push((ques){a[i][k]+a[i][m],i});
	for(i=1;i<=n/2*k;i++){
		tmp=q.top();q.pop();
		h[tmp.id]--;if(h[tmp.id]) q.push((ques){a[tmp.id][m-k+h[tmp.id]]+a[tmp.id][h[tmp.id]],tmp.id}); 
	}
	for(i=1;i<=n;i++){
		for(j=m-k+h[i]+1;j<=m;j++) ans+=a[i][j];
		for(j=1;j<=h[i];j++)ans-=a[i][j];
		for(j=0;j<m;j++) x[i-1][j]=-1;
	}//printf("%d\n",g.size());
	while(!q.empty()) q.pop();
	for(i=1;i<=n;i++) q.push((ques){k-h[i],i});
	for(i=1;i<=k;i++){
		for(j=1;j<=n/2;j++) w[j]=q.top(),q.pop(),x[w[j].id-1][m-w[j].w]=i-1,flag[w[j].id]=1;
		for(j=1;j<=n/2;j++) w[j].w--,q.push(w[j]);
		for(j=1;j<=n;j++){
			if(!flag[j]) x[j-1][--h[j]]=i-1;
			else flag[j]=0;
		} 
	}
	allocate_tickets(x);
	return ans;
}
/*int x;
std::vector<std::vector<int> > sa;
std::vector<int> sb;
 void allocate_tickets(std::vector<std::vector<int> > s){
 	register int i,j,n=s.size(),m=s[0].size();
 	for(i=0;i<n;i++){
 		for(j=0;j<m;j++) printf("%d ",s[i][j]);printf("\n");
	 }
 }
int main(){
	freopen("1.in","r",stdin);
	register int i,j;
	scanf("%d%d%d",&n,&m,&k);
	for(i=1;i<=n;i++){
		sb.clear();
		for(j=1;j<=m;j++) scanf("%d",&x),sb.push_back(x);
		sa.push_back(sb);
	}
	printf("%lld ",find_maximum(k,sa));
}*/
posted @ 2021-02-04 17:57  275307894a  阅读(94)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end