1 条题解
-
0
前言
本来是从树状数组题单来的。
但是看到了这个远古讨论帖,性质就变化了(确信)。
Solution
相信很多人看到这道题的时候和我反应是一样的:
“这……三者出现次数不同的条件是宽泛的,估计答案左右侧应该都是靠近端点的。”
但是又没有一个具体结论。
然后看到了这句话:“最长的子串满足左端点在 到 或右端点在 到 。然后暴力。”
豁然开朗,估计结论是正确的,事实上确实也是。
羡慕大佬的一眼顶针猜结论能力。证明一下吧。
Step 1
容易发现,要证明如上结论,只需证明:
“对于一个合法解 ,只要左右各有 字符,必然有完全包含 的更优解。”
因为这样,只要一个解两端点都不在左右 个,就必能扩展出更优解。
Step 2
为了严谨,还是分类讨论 的组成情况,
由于随便选第一个是长为 的解,所以讨论 长为 无意义,先钦定(?)。
如果 仅由一种字符构成,即出现次数三元组是 ,那么左边加一个,变成 或者 都是满足条件的。
即原解是可以扩展的。
Step 3
如果 由多种字符构成,那出现次数必然互不相同,假设其出现次数三元组 。
设这三个字母为 。
观察在某一边加一个字母的意义:对三元组中一个值加 。
容易发现,如果没有出现三元组相邻两项差值为 ,那我把任意一项加 仍然不会有重复数字出现,即此时随便加一个字母仍合法。
所以相邻两项必然有差值为 ,再分类讨论。
Case 1:
此时 左右两侧字母都只能是 (是按照我上面的加粗假设),否则单选不是 那个必会是新的合法解。
即使两边都是 ,同时选也会变成 是合法解。
Case 2:
此时 左右两侧字母都只能是 ,否则单选不是 那个必会是新的合法解。
即使两边都是 ,同时选也会变成 是合法解。
Case 3:
显然左右必然不是 ,而且如果左右都是 ,同时选后是合法解。
Case 3.1:
此时总的出现次数三元组:,左右两边只能都是 。
即为
单选左边两个得到最左边为 ,单选右边两个得到最右边为 ,
然而对于 ,简单全选后出现次数为 。是合法的解。
Case 3.2:
此时总的出现次数三元组:,左右两边只能都是 。
即为
单选左边两个得到最左边为 ,单选右边两个得到最右边为 ,
然而对于 ,简单全选后出现次数为 。是合法的解。
故对于所有情况总是能扩展出合法解。
证明完毕。
代码是简单的,前缀和预处理后枚举即可。
AC 代码
#include<bits/stdc++.h> using namespace std; int n,i,j,b[1919810],c[1919810],s[1919810],ans; void check(int x,int y) { int cb=b[y]-b[x],cc=c[y]-c[x],cs=s[y]-s[x]; if(cb==0&&cc==0||cs==0&&cb*cc==0) ans=max(ans,y-x); if(cb!=cc&&cc!=cs&&cs!=cb) ans=max(ans,y-x); } string a; int main() { cin>>n>>a;a="6"+a; for(i=1;i<=n;i++) { b[i]=b[i-1]; c[i]=c[i-1]; s[i]=s[i-1]; if(a[i]=='B') b[i]++; if(a[i]=='C') c[i]++; if(a[i]=='S') s[i]++; } for(i=0;i<3;i++) for(j=i+1;j<=n;j++) check(i,j); for(i=n-2;i<=n;i++) for(j=0;j<i;j++) check(j,i); cout<<ans; return 0; }The End.
- 1
信息
- ID
- 6049
- 时间
- 2500ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者