【题解】CF1735E House Planning

题目传送门

分享一个 \(O(n^2)\) 的做法。

首先,不妨假设 \(p_1\)\(0\),枚举到 \(p_1\) 的距离为 \(d_{1,1}\) 的房子到 \(p_2\) 的距离是哪一个,可以得到 \(2n\) 个可能作为答案的 \(p_2\) 值,考虑对于每一个 \(p_2\) 考虑问题是否有解。

容易想到使用二分图匹配的方式判断,若有 \(|p_2-d_{2,i}|=d_{1,j}\)\(p_2+d_{2,i}=d_{1,j}\),即存在房子的位置满足 \(p_1\) 到它的距离为 \(d_{1,i}\)\(p_2\) 到它的距离为 \(d_{2,j}\),则在左侧 \(i\) 点和右侧 \(j\) 点之间连边,若对于一个 \(p_2\) 建出的二分图有完美匹配则有解,根据有哪些匹配边容易得到房子的位置。

因为对于每个点对 \((i,j)\),其至多在两个可行的 \(p_2\) 对应建出的二分图上有连边,所以总边数是 \(O(n^2)\) 的,使用 Dinic 实现二分图匹配的话,复杂度为 \(O(n^2 \sqrt n)\),足以通过本题。

其实这个思路还可以进一步优化,对 \(d\) 序列排序(\(d\) 有序方便下文叙述),把所有 \(d\) 相同的点缩在一起(两边都这么做)进行多重匹配,该点可参与匹配的边数为原本是这个 \(d\) 的点的个数。

那么对于每一个 \(p_2\),其构建出的二分图边数都在 \(2n\) 以内,并且每个点的度数都不超过 \(2\),容易发现在这个限制下,边只会存在链和环形的结构;又因为在 \(i\) 增大时,\(|p_2-d_{2,i}|\) 是先减小后增大的,\(p_2+d_{2,i}\) 是单调增大的,而这又是一个二分图,那么怎么也不可能连出环,这里不详细证明了,可以画图手模理解一下。

那么就只有链了,可以从一端开始对每个左部点直接分配匹配边数,贪心地每次尽量向前(已经分配过的方向)分配,给后面的点留出更多的空间;当一个左部点 \(i\) 的限制无法满足时,说明它前面必有一个左部点 \(j\)(可能就是 \(i\) )使得 \(j\)\(i\) 的这一段中的左部点(包括 \(i,j\))可参与匹配的边数和大于与其相邻的所有右部点的可参与匹配的边数和,根据 Hall 定理,这个图一定没有完美匹配,所以这个贪心方法是正确的。

这个贪心过程显然是线性的,对于 \(i\) 寻找两类边对应的 \(j\) 也可以使用双指针或哈希表做到线性,则对于每个 \(p_2\) 都可以 \(O(n)\) 判断和构造,总时间复杂度 \(O(n^2)\)

实现的时候,由那个单谷和单调性的性质还可以发现:对于一条链,上面所有左部点也是单谷的,所以可以直接倒过来枚举 \(i\) 来做上面那个贪心,相当于从两边向中间做,不用专门把链抠出来搞。

注意 \(p_2=0\) 的时候两类边会连到同一个位置上,可以一开始就判掉;还有题目要求要让答案是非负数,把所有点整体位移一下即可。

主要部分参考代码:

const int N=1010;
int l,n,m,aa[N],bb[N],a[N],ca[N],b[N],cb[N],c[N],d[N],p[N],cnt,ans[N];

inline void add(int v,int x){while(v--) ans[++cnt]=x;}
inline bool chk(int x){
	if(!x) return 0;
	cnt=0;
	for(re int i=1;i<=n;++i) c[i]=ca[i];
	for(re int i=1;i<=m;++i) d[i]=cb[i],p[i]=0;
	for(re int i=1,j=n;i<=m&&x>=b[i];++i){
		while(j&&x-b[i]<a[j]) --j;
		if(j&&x-b[i]==a[j]) p[i]=j;
	}
	for(re int i=1,j=1;i<=m;++i){
		while(j<=n&&b[i]-x>a[j]) ++j;
		if(j<=n&&b[i]-x==a[j]) p[i]=j;
	}
	for(re int i=m,j=n,v;i>=1;--i){
		while(j&&x+b[i]<a[j]) --j;
		if(j&&x+b[i]==a[j]) v=min(d[i],c[j]),d[i]-=v,c[j]-=v,add(v,x+b[i]);
		if(p[i]) v=min(d[i],c[p[i]]),d[i]-=v,c[p[i]]-=v,add(v,x-b[i]);
		if(d[i]) return 0;
	}
	assert(cnt==l),puts("YES");int v=0;
	for(re int i=1;i<=l;++i) v=min(v,ans[i]);
	for(re int i=1;i<=l;++i) printf("%d%c",ans[i]-v," \n"[i==l]);
	printf("%d %d\n",-v,x-v);
	return 1;
}

inline void sol(){
	l=read(),n=m=0,aa[0]=bb[0]=-1;
	for(re int i=1;i<=l;++i) aa[i]=read();
	for(re int i=1;i<=l;++i) bb[i]=read();
	sort(aa+1,aa+l+1),sort(bb+1,bb+l+1);int fl=1;
	for(re int i=1;i<=l;++i) fl&=aa[i]==bb[i];
	if(fl){
		puts("YES");
		for(re int i=1;i<=l;++i) printf("%d%c",aa[i]," \n"[i==l]);
		puts("0 0");
		return ;
	}
	for(re int i=1;i<=l;++i) if(aa[i]!=aa[i-1]) a[++n]=aa[i],ca[n]=1;else ++ca[n];
	for(re int i=1;i<=l;++i) if(bb[i]!=bb[i-1]) b[++m]=bb[i],cb[m]=1;else ++cb[m];
	for(re int i=1;i<=m;++i) if(chk(abs(aa[1]-b[i]))||chk(aa[1]+b[i])) return ;
	puts("NO");
}
posted @ 2022-10-13 10:45  WhaleAtCola  阅读(70)  评论(1编辑  收藏  举报