E - 11/22 Subsequence 解説
by
physics0523
別解
以下、 \(Q=1,L=1,R=|S|\) として説明します。クエリでもやることは同じです。
まず、 / の位置を全てリストアップします。
その上で、 / のリストに対して以下の二分探索が成立します。
- ある
/の左側にある \(1\) の数を \(C_1\) 、右側にある \(2\) の数を \(C_2\) とする。 - もし \(C_1 > C_2\) であれば、より左にある
/を探索すべきである。 - そうでないなら、より右にある
/を探索すべきである。
なぜなら、 \(C_1,C_2\) のうち大きくない方がボトルネックになっており、それを増やさない限り解は改善しないからです。
これらの操作は累積和や二分探索で実装することができ、全体で時間計算量 \(O(N + Q \log N)\) でこの問題に正解できます。
実装例 (C++):
#include<bits/stdc++.h>
using namespace std;
int lb(int x,vector<int> &a){
int l=0,r=((int)a.size())-1;
while(l<=r){
int te=(l+r)/2;
if(a[te]<x){l=te+1;}
else{r=te-1;}
}
return l;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,q;
cin >> n >> q;
string s;
cin >> s;
vector<int> c1(n+1,0);
vector<int> c2(n+1,0);
vector<int> sl;
for(int i=0;i<n;i++){
c1[i+1]=c1[i];
c2[i+1]=c2[i];
if(s[i]=='1'){
c1[i+1]++;
}
else if(s[i]=='2'){
c2[i+1]++;
}
else{
sl.push_back(i+1);
}
}
// for(auto &nx : c1){
// cout << nx << " ";
// }cout << "\n";
// for(auto &nx : c2){
// cout << nx << " ";
// }cout << "\n";
// for(auto &nx : sl){
// cout << nx << " ";
// }cout << "\n";
while(q>0){
q--;
int l,r;
cin >> l >> r;
int lef=lb(l,sl);
int rig=lb(r+1,sl);
if(lef==rig){
cout << "0\n";
continue;
}
rig--;
int res=0;
while(lef<=rig){
int te=(lef+rig)/2;
int pt=sl[te];
int l1=(c1[pt]-c1[l-1]);
int r2=(c2[r]-c2[pt]);
res=max(res,2*min(l1,r2)+1);
if(l1>r2){rig=te-1;}
else{lef=te+1;}
}
cout << res << "\n";
}
return 0;
}
投稿日時:
最終更新:
