题解:P12765 [POI 2018 R3] 三座塔 2 Three towers 2
wjs_jwl_zhy · · 题解
题解没满,水一发。
首先发现优化查询比较困难,考虑优化查询范围,这里有一个结论:最终答案一定要么左端点在最左的三个之一,要么右端点在最右的三个之一。
怎么证明呢?给出一个证明方法:
当不合法是三个字符出现次数全部相等时,向外扩展一个一定会形成一个更长的且合法的串。
当有两个字符出现次数相等时,若待扩展的字符是这两个之一,扩展后一定更长且合法。现在考虑带扩展的字符是第三种的情况,分两种情况讨论:
第三个字符的出现次数比这两种出现次数少 1。那么扩展后就转化为了三个字符出现次数全部相等的情况,一定可以再次扩展。 第三个字符出现次数比这两种出现次数少不止 1。那么这个区间可能无解,排除。
综上,结论是对的。
前缀和一下就是线性的了,我写了个粗劣的树状数组。
:::info[代码]
#include<bits/stdc++.h>
#define int long long
using namespace std;
int m,n;
int tree[1000001][4],ans;
int law(int a){
return a&(-a);
}
int add(int u,int s,int k){
while(u<=n){
tree[u][k]+=s;
u+=law(u);
}
return 0;
}
int qu(int u,int k){
int s=0;
while(u>0){
s+=tree[u][k];
u-=law(u);
}
return s;
}
signed main(){
cin>>n;
char c;
for(int i=1;i<=n;i++){
cin>>c;
if(c=='B') add(i,1,0);
if(c=='C') add(i,1,1);
if(c=='S') add(i,1,2);
}
for(int i=1;i<=3;i++){
for(int j=i;j<=n;j++){
if(j>=i){
int s[4];
for(int k=0;k<=2;k++) s[k]=qu(j,k)-qu(i-1,k);
sort(s,s+3);
if(s[0]!=s[1]&&s[1]!=s[2]&&s[2]!=s[0]) ans=max(ans,j-i+1);
}
}
}
for(int i=1;i<=n;i++){
for(int j=n-2;j<=n;j++){
if(j>=i){
int s[4];
for(int k=0;k<=2;k++) s[k]=qu(j,k)-qu(i-1,k);
sort(s,s+3);
if(s[0]!=s[1]&&s[1]!=s[2]&&s[2]!=s[0]) ans=max(ans,j-i+1);
}
}
}
if(ans==0){
cout<<"NIE";
return 0;
}
cout<<ans;
return 0;
}
:::