[Codeforces 1199C]MP3(离散化+二分答案)

[Codeforces 1199C]MP3(离散化+二分答案)

题面

给出一个长度为n的序列(a_i)和常数I,定义一次操作[l,r]可以把序列中<l的数全部变成l,>r的数全部变成r。每次操作的代价为改变数的个数。问:要让操作后序列里不同数的个数(k)满足$n lceil log _2 k ceil leq 8I $,操作的最小代价

分析

首先把a离散化,这样[l,r]都在(10^5)的级别。枚举l,发现r显然有单调性。操作后不同数的个数随r的增大而减小(考虑极端情况l=r,操作后所有数相同。若(r_1)满足条件,那(r_2<r_1)也一定满足条件。而操作的最小代价随r增大而减小。所以我们只需要对于每个l找到最大的满足条件的r即可。

考虑判定:我们把a排序,记(cnt(l,r))表示满足(a_i in [l,r])(i)的个数,m为离散化后的最大值。那么操作的代价为(cnt(1,l-1)+cnt(r+1,m)),不同数的个数为(r-l+1)(操作完之后最小值变成l,最大值变成r,由于我们离散化过,[l,r]内的数一定是连续的,所有个数为[r-l+1])

那么我们只要预处理前缀和(sum(v))表示整个序列值<=v的个数,离散化后可以(O(n))预处理,(cnt(l,r)=sum(r)-sum(l-1))

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define INF 0x3f3f3f3f
#define maxn 800000
using namespace std;
int n,m,I;
int a[maxn+5];
int b[maxn+5];
int sum1[maxn+5];
 
inline int cnt(int l,int r){
	return sum1[r]-sum1[l-1];
}
int check(int lb,int rb){
	int ans1=cnt(1,lb-1);
	int ans2=cnt(rb+1,m);
	int k=rb-lb+1;
//	int cnt=2+sum1[lb+1]+sum1[rb-1];
	if(ceil(log2(k))*n<=I*8) return ans1+ans2;
	else return -1;
}
int bin_search(int t){
 
	int l=t,r=m;
	int ans=INF;
	int mid;
	while(l<=r){
		mid=(l+r)>>1;
		int val=check(t,mid);
		if(val!=-1){
			ans=val;
			l=mid+1;
		}else r=mid-1;
	}
	return ans;
}
 
int mark[maxn+5];
int main(){
//	freopen("input.txt","r",stdin);
	scanf("%d",&n);
	scanf("%d",&I);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	m=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+m,a[i])-b;
	for(int i=1;i<=n;i++){
		sum1[a[i]]++;
	}
	for(int i=1;i<=m;i++) sum1[i]+=sum1[i-1];
//	printf("%d
",m);
	int ans=INF;
	for(int l=1;l<=m;l++){
		int tmp=bin_search(l);
		ans=min(ans,tmp);
	}
	printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/birchtree/p/11273809.html