题解 P9285 [AGM 2023 资格赛] YsaeSort
先来一个不是很正经的
首先考虑显然有一个简单的
然后考虑怎么优化,因为每次操作
这样做到
事实上观察到上面这种情况每次合并的有序段不会很多,所以我们考虑当有序段个数小于一个很小的常数
inline void merge(multiset<int>&x,multiset<int>&y)
{
if (x.size()<y.size()) x.swap(y);
for (auto u:y) x.insert(u);multiset<int>().swap(y);
}
void BellaKira()
{
cin>>n;
for (int i=1;i<=n;i++)
{
cin>>a[i];
s[i].insert(a[i]);
S.insert(i);
}
S.insert(n+1);
cin>>q;
ll all=0;
int ooo=0;
while (q--)
{
int opt,l,r;
cin>>opt;
if (opt==1)
{
cin>>l>>r;
r=min(r,n);
auto it=S.lower_bound(l);
if (it==S.end()) continue;
it++;
auto itl=it;
int t=0;
while (it!=S.end())
{
if ((*it)>r) break;
merge(s[l],s[*it]);
it++;
t++;
}
if (t>=3)
{
S.erase(itl,it);
int o=l-1;
for (auto u:s[l])
{
a[++o]=u;
}
} else
{
it=itl;
while (it!=S.end())
{
if ((*it)>r) break;
auto it1=it;
it1++;
// cout<<"???"<<l<<" "<<(*it)<<" "<<(*it1)<<endl;
inplace_merge(a+l,a+(*it),a+(*it1));
it++;
}
S.erase(itl,it);
}
} else
{
cin>>l>>r;
int mx=0;
ll ans=0;
for (int i=l;i<=r;i++)
{
if (mx>a[i])
ans=max(ans,1ll*a[i]*mx);
mx=max(mx,a[i]);
}
all^=ans;
cout<<ans<<'\n';
}
}
}
正经做法是个稍微有点 trivial 的东西,但是不那么好写。
考虑没有修改时的一个做法,就是我们每次线段树上维护一个 pair 表示当前节点答案以及当前节点的
那考虑有了排序,我们需要维护整个
其实并不需要维护,如果我们维护了每个有序段,那么
那就好办了,正好跟我们需要做的事情是一样的。
我们考虑对于两个点
那么每次排序相当于合并有序段,然后把当前这个区间内的所有节点答案设成
线段树上合并两个 pair 也很简单,由于之前已经知道了:某个区间是可以表示成若干个整个有序段和“某个有序段的大于等于某个数的集合或小于等于某个数的集合”。所以我们直接把右边这个区间分成:某个有序段的大于等于某个数的集合、若干个连续的整个的有序段、某个有序段的小于等于某个数的集合。然后在这三个部分上查询跟
时间复杂度