[atAGC062D]Walk Around Neighborhood

\(D=\max_{1\le i\le n}d_{i}\),则无解当且仅当\(2D>\sum_{i=1}^{n}d_{i}\)

结论:\(\forall (x,y),\exists (X,Y),\begin{cases}|X|+|Y|=R\\|x-X|+|y-Y|=d\end{cases}\) 当且仅当\(|r-R|\le d\le r+R\)(其中\(r=|x|+|y|\)

必要性:根据\(|a|-|b|\le |a-b|\le |a|+|b|\),显然成立

充分性:根据对称性,不妨假设\(x,y\ge 0\)

  • \(d=r+R\),则取\((X,Y)=(-R,0)\)即可
  • \(d=|r-R|\),则取\((X,Y)=\begin{cases}(x+(R-r),y)&r\le R\\(x-(r-R),y)&r>R\and r-R\le x\\(0,y-((r-R)-x))&r>R\and r-R>x\end{cases}\) 即可

由于\(\{(X,Y)\mid |X|+|Y|=R\}\)构成正方形,进而\(|x-X|+|y-Y|\)的取值构成区间,即得证

根据结论,问题即转化为以下形式:

选择排列\(d_{i}\)\(r_{i}\),要求\(\begin{cases}r_{0}=r_{n}=0\\\forall i\in [1,n],|r_{i-1}-r_{i}|\le d_{i}\le r_{i-1}+r_{i}\end{cases}\),并最小化\(\max_{1\le i\le n}r_{i}\)

二分枚举答案\(s\),显然有解的必要条件为\(s\ge \frac{D}{2}\)

当确定\(d_{i}\)后,每个\(r_{i}\)的取值构成区间,转移为\([x,y]\rightarrow [\min_{x\le i\le y}|i-d|,\min(y+d,s)]\)

  • 对于\(d\le s\),必然可以转移,且\(y=s\)时转移后为\([\max(x-s,0),y]\)
  • 对于\(d>s\),转移时需有\(y\ge d-s\),且转移后为\([d-y,s]\)

\(a,b\)分别为最小/最大的\(i\)满足\(d_{i}>s\),则应有\(\begin{cases}\sum_{i=1}^{a-1}d_{i}\ge d_{a}-s\\\sum_{i=b+1}^{n}d_{i}\ge d_{b}-s\end{cases}\),且其余\(d_{i}\)均存在合适的位置

可以贪心取\(d_{a},d_{b}\)\(d_{i}>s\)的最、次小值(若唯一则均为\(D\)),即转化为对\(d_{i}\le s\)的背包

具体的,记\(S=\sum_{d_{i}\le s}d_{i}\),即判断是否存在子集和\(\in [d_{a}-s,S-(d_{b}-s)]\)

当区间长度\(\ge s\)时必然有解,进而\(S-(d_{b}-s)-(d_{a}-s)+1<s\),放缩得\(S\le 2D\)

根据经典结论,不同的\(d_{i}\)个数为\(O(\sqrt{D})\),并可以利用加入过程将二分改为枚举,时间复杂度为\(O(D\sqrt{D})\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005;
int n,s,ans,a[N],cnt[N],g[N<<1],f[N<<1];
ll sum;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		cnt[a[i]]++;
	}
	sort(a+1,a+n+1);
	for(int i=1;i<n;i++)sum+=a[i];
	if (sum<a[n]){
		puts("-1");
		return 0;
	}
	s=sum=0,ans=a[n],f[0]=1;
	for(int i=1;i<a[n];i++){
		if (cnt[i]){
			s+=cnt[i];
			if (sum<=(a[n]<<1)){
				for(int j=sum;j;j--)f[j]-=f[j-1];
			}
			sum+=(ll)i*cnt[i];
			if (sum<=(a[n]<<1)){
				int s=i*(cnt[i]+1);
				for(int j=0;j<=sum;j++)g[j]=f[j]+(j<i ? 0 : g[j-i]);
				for(int j=0;j<=sum;j++)f[j]=(g[j]>(j<s ? 0 : g[j-s]));
				for(int j=1;j<=sum;j++)f[j]+=f[j-1];
			}
		}
		if (i>=(a[n]>>1)){
			if (sum>(a[n]<<1)){ans=i;break;}
			int x=a[s+1]-i,y=sum-(a[min(s+2,n)]-i);
			if ((x<=y)&&(f[y]>f[x-1])){ans=i;break;}
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2023-06-30 09:02  PYWBKTDA  阅读(107)  评论(0编辑  收藏  举报