Codeforces 1091E. New Year and the Acquaintance Estimation 二分+结论

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_36797646/article/details/85473689

题解:

第一次做这种……题面放个链接让你自己去看题解的题目……
链接告诉你的是一个非常有用的结论:
对于从大到小排好序的一些度数 d d ,能够合法当且仅当:
1、 i = 1 n d i \sum_{i=1}^nd_i 为偶数。
2、对于 1 k n 1\le k\le n ,都有 i = 1 k d i k ( k 1 ) + i = k + 1 n min ( k , d i ) \sum_{i=1}^kd_i\le k(k-1)+\sum_{i=k+1}^n\min(k,d_i)
有了这个结论,我们又会得到另外一个结论:答案一定是连续一段偶数或者奇数。
所以考虑求出某个合法的答案,然后就能二分出左右端点了。
但是求出某个合法答案是不能直接二分的,因为它是中间的某一段。
但其实还是可以二分的。观察一下式子,记二分出的值在排序后的 d d 中的位置为 p o s pos ,显然当第一个不合法位置在 p o s pos 前时,这个值太小;否则这个值就太大了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=500010;
const int inf=2147483647;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,s=0,pos[Maxn];LL sum[Maxn],d[Maxn],D[Maxn];
bool cmp(int x,int y){return x>y;}
int check(int o)//1 太大 0 合法 -1 太小 
{
	int m=0,tmp;
	for(int i=1;i<=n+1;i++)
	if(o>=D[i])
	{
		for(int j=1;j<i;j++)d[++m]=D[j];
		d[++m]=o;tmp=m;
		for(int j=i;j<=n;j++)d[++m]=D[j];
		break;
	}
	sum[0]=0;
	for(int i=1;i<=m;i++)sum[i]=sum[i-1]+d[i];
	int c=1;
	for(int i=m;i;i--)
	{
		while(c<=m&&d[i]>c)pos[c++]=i;
	}//pos[i] 右起第一个>i的位置 
	for(int i=1;i<=m;i++)
	{
		LL L=sum[i];
		LL R=(LL)i*i-i+(LL)max(0,pos[i]-i)*i+sum[m]-sum[pos[i]];
		if(L>R)
		{
			if(i<tmp)return -1;
			return 1;
		}
	}
	return 0;
}
int main()
{
	n=read();d[n+1]=-inf;
	for(int i=1;i<=n;i++)D[i]=read(),s^=(D[i]&1);
	sort(D+1,D+1+n,cmp);
	int l=0,r=n>>1;
	if((s&1)&&(n&1))r++;
	int w=-1;
	while(l<=r)
	{
		int mid=l+r>>1,t=(mid<<1)+s,v=check(t);
		if(v==0){w=t;break;}
		if(v==1)r=mid-1;
		else l=mid+1;
	}
	if(w==-1)return puts("-1"),0;
	int L,R;
	l=0,r=w>>1;
	if((s&1)&&(w&1))r++;
	while(l<=r)
	{
		int mid=l+r>>1,t=(mid<<1)+s;
		if(check(t)==0)r=mid-1;
		else l=mid+1;
	}
	L=((r+1)<<1)+s;
	l=w>>1,r=n>>1;
	if((s&1)&&(w&1))l++;
	if((s&1)&&(n&1))r++;
	while(l<=r)
	{
		int mid=l+r>>1,t=(mid<<1)+s;
		if(check(t)==0)l=mid+1;
		else r=mid-1;
	}
	R=((l-1)<<1)+s;
	for(int i=L;i<=R;i+=2)printf("%d ",i);
}

猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/85473689
今日推荐