JOI Open 2022 黑白点

sto hehezhou orz。

Link

首先,下面这种互相不交但是黑白同向的结构一定是严格不优的。(蒯的 hehezhou 的图)

hehezhou 的图

也就是说不断调整上述结构一定能调整到不存在上述结构。

画画图分讨下相邻两个黑点的匹配关系,发现它们必定匹配两个相邻白点,否则都会形成上述结构。

相当于将两个数组循环移位后匹配。

考虑快速求出相交对的个数,在这类题中有一种重要思想,就是拆贡献到每一个部分后证明每一个部分都能达到上界。

这道题我们发现对于一条长为 \(l(l \leq n)\) 的线段,与它相交的线段个数至多为 \(l\)。同时利用上述性质,调整掉严格劣的结构发现这个界是一定可以达到的(考虑设短的一侧有 \(x\) 个黑点 \(y\) 个白点讨论易得)。

转化成最大化匹配的 \(\sum l\),然而这个式子仍不太好最优化。

我们反过来考虑无法相交的对,相当于最小化 \(\sum n-l\)。我们可以考虑将所有的黑点移到正对面,这样原来相距 \(l\) 的现在相距 \(n-l\)

那么问题转化成了在环上黑白匹配,最小化匹配长度。

考虑如何做序列上的问题,依然把点摊到数轴上,那么数轴上一条边的贡献最少为同一侧黑点和白点数量之差。容易证明该贡献可以达到。

那么把序列当成折线,黑点 \(\texttt{+1}\),白点 \(\texttt{-1}\),那么答案相当于坐标的绝对值之和。

环上的问题就很平凡了,相当于把折线从某个点断开,交换然后拼接,可以直接看作将折线上下平移,最小化 \(\sum {|y_i-x|}\),取中位数即可。

#include <cstdio>
#include <algorithm>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int N=1000003;
char s[N];
int a[N];
int main(){
	int n=read();
	int nn=n<<1;
	scanf("%s",s+1);
	for(int i=1;i<=nn;++i){
		if(s[i]=='W') ++a[i];
		else if(i>n) --a[i-n];
		else --a[i+n];
	}
	for(int i=1;i<=nn;++i) a[i]+=a[i-1];
	nth_element(a+1,a+n,a+nn+1);
	long long res=(long long)n*(n-1);
	for(int i=1;i<=nn;++i){
		if(a[i]<a[n]) res-=a[n]-a[i];
		else res-=a[i]-a[n];
	}
	printf("%lld\n",res>>1);
	return 0;
}
posted @ 2023-01-14 08:34  yyyyxh  阅读(73)  评论(0编辑  收藏  举报