Luogu U696097 有趣的点名 题解

2026.6.27 注 : 解法和解法证明来自豆包 AI , 本题解的所有文本、代码由本人编写 , 所有图片由本人制作 .

分析

设字符串 \(S\) 中的某一段由字符串 \(S_a\) , \(S_b\) , \(S_c\) 拼接而成 .

如图所示 :

图中蓝色矩形部分表示一个字符串首尾部的 " B " 连续段 , 绿色矩形部分表示一个字符串中部的 " B " 连续段 , 红色矩形部分表示一个字符串尾部的 " B " 连续段与后一个字符串首部的 " B " 连续段拼接而成的 " B " 连续段 .

绿色矩形部分对答案的贡献与字符串排列方式无关 , 而红色矩形部分对答案的贡献与字符串排列方式有关 .

设字符串 \(S_a\) 尾部的 " B " 连续段长度为 \(A\) , 字符串 \(S_b\) 首部的 " B " 连续段长度为 \(B\) , 则图中靠左的红色矩形部分对答案的贡献为 \(\lfloor \frac{A+B}{2} \rfloor\) . 同理可推得其他红色矩形部分对答案的贡献 .

在此有恒等式

\[\lfloor x+y \rfloor=\lfloor x \rfloor+\lfloor y \rfloor+\lfloor \{x\}+\{y\} \rfloor \]

\(\{x\}\) 即 \(x\) 的小数部分 .

设字符串 \(S_i\) 首部的 " B " 连续段长度为 \(start_i\) , 尾部的 " B " 连续段长度为 \(end_i\) , 中部的 " B " 连续段长度为 \(mid_i\) , 则答案为 :

\[\begin{aligned} & \ \ \ \ \ \lfloor \frac{start_1}{2} \rfloor+ \lfloor \frac{end_n}{2} \rfloor+ \sum_{i=1}^{n-1} \lfloor \frac{end_i+start_{i+1}}{2} \rfloor+ \sum_{i=1}^{n} \lfloor \frac{mid_i}{2} \rfloor \\ &= \lfloor \frac{start_1}{2} \rfloor+ \lfloor \frac{end_n}{2} \rfloor+ \sum_{i=1}^{n-1} \lfloor \frac{end_i}{2}+ \frac{start_{i+1}}{2} \rfloor+ \sum_{i=1}^{n} \lfloor \frac{mid_i}{2} \rfloor \\ &= \lfloor \frac{start_1}{2} \rfloor+ \lfloor \frac{end_n}{2} \rfloor+ \sum_{i=1}^{n-1} (\lfloor \frac{end_i}{2} \rfloor + \lfloor \frac{start_{i+1}}{2} \rfloor+ \lfloor \{\frac{end_i}{2}\}+ \{\frac{start_{i+1}}{2}\} \rfloor)+ \sum_{i=1}^{n} \lfloor \frac{mid_i}{2} \rfloor \\ &= \sum_{i=1}^{n} (\lfloor \frac{start_i}{2} \rfloor+ \lfloor \frac{end_i}{2} \rfloor+ \lfloor \frac{mid_i}{2} \rfloor)+ \sum_{i=1}^{n-1} (\lfloor \{\frac{end_i}{2}\}+\{\frac{start_{i+1}}{2}\} \rfloor) \end{aligned} \]

设该式第一项为常数 \(C\) , 第二项为变量 \(K\) . 当变量 \(K\) 最小时 , 答案最小 .

通过观察 , \(\{\frac{end_i}{2}\}\) , \(\{\frac{start_{i+1}}{2}\}\) ∈ \(\{0,0.5\}\) . 当两者均为 \(0.5\) 时 , 会对答案贡献 \(1\) , 所以应尽量减少此类组合 , 以使变量 \(K\) 最小 .

为方便表示 , 将 \(0.5\) 表示为 " I " , \(0\) 表示为 " O " .例如某个数对 \((\{\frac{start_i}{2}\} , \{\frac{end_i}{2}\})\) 为 \(\{0,0.5\}\) , 则其可表示为 OI .

设 OI 的数量为 \(cnt_{OI}\) , 其余可类推 .

定理 \(1\)

对于任意一种字符串排列方式 , 有 \(K \geq \max(0,cnt_{II}-cnt_{OO}-1)\) .

证明

一 . 当 \(cnt_{II} \gt 0\) 时

可以将排列分为三个部分考虑 .

$1 . $ II 连续段内

设排列中有 \(m\) 个 II 连续段 , 第 \(j\) 段有 \(num_j\) 个 II , 那么 \(\sum_{j=1}^{m} num_j=cnt_{II}\) .

第 \(j\) 段对 \(K\) 贡献 \(num_j-1\) , 那么 \(m\) 段对 \(K\) 贡献

\[K_内=\sum_{j=1}^{m} (num_j-1)=cnt_{II}-m \]

$2 . $ II连续段间

若两段间没有 OO , 则两段间一定有 OI 或 IO , 此时至少有一对 I 和 I相邻 ( 可能为 OI IO 或 OI II 或 II IO ) , 并对 \(K\) 至少贡献 \(1\) .

若两段间含有至少一个 OO , 则两段间对 \(K\) 贡献的下界为 \(0\) .

排列中有 \(m-1\) 个间隔 , 设其中有 \(t\) 个间隔含有至少一个 OO , 则有 \(m-1-t\) 个间隔对 \(K\) 至少贡献 \(1\) , 此时对 K 贡献

\[K_间 \geq m-1-t \]

$3 . $ 最左端 II 连续段的左侧与最右端 II 连续段的右侧

两侧对 \(K\) 的贡献一定非负 , 即

\[K_{两侧} \geq 0 \]


综上所述 ,

\[K=K_内+K_间+K_{两侧} \]

\[K \geq (cnt_{II}-m)+(m-1-t)+0 \]

\[K \geq cnt_{II}-1-t \]

因为一个 OO 只被一个间隔或两侧包含 , 且一个含有 OO 的间隔或两侧至少含有一个 OO , 所以 \(t \leq cnt_{OO}\) , 将其代入 , 得

\[K \geq cnt_{II}-cnt_{OO}-1 \]

因为 \(K\) 一定非负 , 所以

\[K \geq \max(0 , cnt_{II}-cnt_{OO}-1) \]


二 . 当 \(cnt_{II}=0\) 时

此时 , \(cnt_{II}-cnt_{OO}-1 \lt 0\) , \(K_内=0\) , \(K_间=0\) , \(K_{两侧} \geq 0\) , 则 \(K \geq 0\) , 与定理 \(1\) 一致 .


证毕 .

定理 \(2\)

存在字符串排列方式,使得 \(K=\max(0 , cnt_{II}-cnt_{OO}-1)\) .

证明

一种构造方式如下 :

左侧放置所有 IO ; 中部按照 II OO 的顺序交替放置 ( 若两者中一者会先放完 , 则另一者连续放置 ) ; 右侧放置所有 OI .


上述构造方式中 , 仅在 OO 用尽 , 形成 II 连续段时 , 段内会对 \(K\) 有贡献 .

结合定理 \(1\) 的证明 , 段内对 \(K\) 贡献 \(cnt_{II}-m\) .

$1 . $ 当 \(cnt_{II}-1 \geq cnt_{OO}\) 时

此时每个 II 不能自成一段 , II 连续段数量 \(m=cnt_{OO}+1\) , 将其代入 , 得

\[K=cnt_{II}-(cnt_{OO}+1)=cnt_{II}-cnt_{OO}-1 \]

$2 . $ 当 \(cnt_{II}-1 \lt cnt_{OO}\) 时

此时每个 II 能自成一段 , II 连续段数量 \(m=cnt_{II}\) , 将其代入 , 得

\[K=cnt_{II}-cnt_{II}=0 \]


综上所述 , \(K=\max(0 , cnt_{II}-cnt_{OO}-1)\) .

证毕 .


结合定理 \(1\) 和定理 \(2\) , 可知答案为

\[\sum_{i=1}^{n} (\lfloor \frac{start_i}{2} \rfloor+ \lfloor \frac{end_i}{2} \rfloor+ \lfloor \frac{mid_i}{2} \rfloor)+ \max(0 , cnt_{II}-cnt_{OO}-1) \]

其中各项均为定值 , 与字符串排列方式无关 .

该解法时间复杂度为 \(O(|S|)\) .


代码

#include <bits/stdc++.h>

using namespace std;

int n;
string S;
int cnt[2][2];
long long ans;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  cin>>n;
  for(int i=1;i<=n;++i) {
    cin>>S;

    int len=S.size(),l=0,r=len-1;

    while(S[l]=='B') ++l;
    while(S[r]=='B') --r;

    ans+=l/2,ans+=(len-r-1)/2; // 求floor(start_i/2)和floor(end_i/2)

    for(int j=l;j<=r;) {
      while(j<=r&&S[j]!='B') ++j;
      int tmp=j;
      while(j<=r&&S[j]=='B') ++j;

      ans+=(j-tmp)/2;
    } // 求floor(mid_i/2)

    ++cnt[l%2][(len-r-1)%2]; // 记录四种类型的字符串数量
  }

  cout<<ans+max(0,cnt[1][1]-cnt[0][0]-1);

  return 0;
}
posted @ 2026-06-27 11:00  __Szr1  阅读(20)  评论(3)    收藏  举报