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