1. 程式人生 > >Codeforces 1091E. New Year and the Acquaintance Estimation 二分+結論

Codeforces 1091E. New Year and the Acquaintance Estimation 二分+結論

題解:

第一次做這種……題面放個連結讓你自己去看題解的題目……
連結告訴你的是一個非常有用的結論:
對於從大到小排好序的一些度數 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);
}