Codeforces 1091E. New Year and the Acquaintance Estimation 二分+結論
阿新 • • 發佈:2019-01-08
題解:
第一次做這種……題面放個連結讓你自己去看題解的題目……
連結告訴你的是一個非常有用的結論:
對於從大到小排好序的一些度數
,能夠合法當且僅當:
1、
為偶數。
2、對於
,都有
。
有了這個結論,我們又會得到另外一個結論:答案一定是連續一段偶數或者奇數。
所以考慮求出某個合法的答案,然後就能二分出左右端點了。
但是求出某個合法答案是不能直接二分的,因為它是中間的某一段。
但其實還是可以二分的。觀察一下式子,記二分出的值在排序後的
中的位置為
,顯然當第一個不合法位置在
前時,這個值太小;否則這個值就太大了。
程式碼:
#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);
}