Автор проблеми: | TheSensei |
Підготовлено: | Hasan Saleh (MathModel) |
Редаговано: | Hasan Saleh (MathModel) |
Умова задачі
Дано масив довжини , розділіть його на три непорожні суміжні сегменти:
Сегмент 1: a_1, a_2, \dots, a_i
Сегмент 2: a_{i+1}, a_{i+2}, \dots, a_j
Сегмент 3: a_{j+1}, a_{j+2}, \dots, a_n
Знайдіть індекси i<j та ціле число таке, що
Якщо таке розділення існує, виведіть один дійсний (i,j,x); в іншому випадку виведіть .
Основна ідея полягає в тому, щоб помітити, що побітове OR сегмента додатних цілих чисел є зростаючим. Це справжня властивість, з цим фактом ми можемо попередньо обчислити префікс-AND, нехай це буде , і суфікс-XOR, нехай це буде . Ми можемо ітерувати через i_{й} префікс побітового AND і виконати бінарний пошук для першого j>i так, що
. Як тільки ми знайшли такий індекс , ми перевіряємо, чи також збігається s_{n-1-j}; якщо так, то ми виводимо (i,j,p_i), інакше продовжуємо шукати, поки не дослідимо всі варіанти.
Побітове OR деякого підмасиву можна ефективно попередньо обчислити, використовуючи Дерево Сегментів або Розріджену Таблицю. Наступна реалізація використовує Дерево Сегментів.
Складність \mathcal{O}(n\log^2n)
#include <bits/stdc++.h>
using namespace std;
#define int long long
vector<int> t;
void build(int v,int l,int r,vector<int>&a){
if(l==r) t[v]=a[l];
else{
int m=(l+r)/2;
build(v*2,l,m,a);
build(v*2+1,m+1,r,a);
t[v]=t[v*2]|t[v*2+1];
}
}
int query(int v,int l,int r,int ql,int qr){
if(ql>r||qr<l) return 0;
if(ql<=l&&r<=qr) return t[v];
int m=(l+r)/2;
return query(v*2,l,m,ql,qr)|query(v*2+1,m+1,r,ql,qr);
}
void solve(){
int n;cin>>n;
vector<int>a(n);
for(int i=0;i<n;i++)cin>>a[i];
vector<int>p(n);
p[0]=a[0];
for(int i=1;i<n;i++)p[i]=p[i-1]&a[i];
vector<int>s(n);
s[0]=a[n-1];
for(int k=1;k<n;k++)s[k]=s[k-1]^a[n-1-k];
t.assign(4*n,0);
build(1,0,n-1,a);
for(int i=0;i<n-2;i++){
int cur=p[i];
int l=i+1,r=n-2,j1=-1;
while(l<=r){
int mid=(l+r)/2;
int val=query(1,0,n-1,i+1,mid);
if(val<cur) l=mid+1;
else{
if(val==cur) j1=mid;
r=mid-1;
}
}
if(j1==-1) continue;
l=j1; r=n-2;
int j2=n-1;
while(l<=r){
int mid=(l+r)/2;
int val=query(1,0,n-1,i+1,mid);
if(val>cur){j2=mid;r=mid-1;}
else l=mid+1;
}
for(int j=j1;j<j2;j++){
if(s[n-2-j]==cur){
cout<<i+1<<" "<<j+1<<" "<<cur<<"\n";
return;
}
}
}
cout<<"-1\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;cin>>T;
while(T--)solve();
return 0;
}