JOI Open 2022 黑白点
sto hehezhou orz。
Link。
首先,下面这种互相不交但是黑白同向的结构一定是严格不优的。(蒯的 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;
}