我们有一组n
正整数和m
quires。
每个查询可以是以下两种类型之一。
[i, j]
[i, j]
中给出的元素重新排序,使得数组元素将是这样的 (a[i+1],a[i],a[i+3],a[i+2],..............,a[j],a[j-1])
假设这个范围长度是均匀的。其中a[i]
是i
索引处的数组元素。
以下是限制
2 ≤ n,m ≤ 2×10^5
1 ≤ a[i] ≤ 10^6
1≤ i ≤ j ≤n
我尝试使用细分树,但这些时间超过2秒。 任何人都可以建议最佳数据结构或任何最佳方法吗?
这是我的代码
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int sgtree[524288];
int data[200005];
int treeind[200005];
int constructSgTree(int start,int end,int index){
if(start==end){
sgtree[index]=data[start];
treeind[start]=index;
return data[start];
}
else{
int mid=start+(end-start)/2;
sgtree[index]=constructSgTree(start,mid,index*2+1)+constructSgTree(mid+1,end,index*2+2);
return sgtree[index];
}
}
int sumSgTree(int start,int end,int i,int j,int index){
if(i<=start&&j>=end){
return sgtree[index];
}
else if(i>end||j<start){
return 0;
}
else{
int mid=start+(end-start)/2;
return sumSgTree(start,mid,i,j,2*index+1)+sumSgTree(mid+1,end,i,j,2*index+2);
}
}
void updateSgTree(int start,int end,int i,int val,int index){
if(i<start||i>end){
return ;
}
else{
sgtree[index]+=val;
if(start!=end)
{
int mid=start+(end-start)/2;
updateSgTree(start,mid,i,val,2*index+1);
updateSgTree(mid+1,end,i,val,2*index+2);
}
}
}
int main() {
int n,i,q,op,l,r,temp,j,temp1,temp2,temp3;
cin>>n>>q;
float sum=0;
for(i=0;i<n;i++){
cin>>data[i];
//dataind[i]=i;
}
constructSgTree(0,n-1,0);
/*
for(i=0;i<n;i++){
cout<<sgtree[treeind[i]]<<" ";
}
*/
//cout<<endl;
for(i=0;i<q;i++){
cin>>op>>l>>r;
l--;
r--;
if(op==2){
j=l;
/*
sum=0.0;
while(j<=r){
/*
temp=data[j]-sgtree[treeind[j]];
if(temp!=0){
updateSgTree(0,n-1,j,temp,0);
}
sum+=data[j];
j++;
}
cout<<sum<<endl;
*/
cout<<sumSgTree(0,n-1,l,r,0)<<endl;
}
else{
while(l<=r){
//temp=data[l+1]-data[l];
if(l!=r){
temp=sgtree[treeind[l]];
sgtree[treeind[l]]=sgtree[treeind[l+1]];
sgtree[treeind[l+1]]=temp;
temp1=(treeind[l]-1)/2;
temp2=(treeind[l+1]-1)/2;
while(temp1!=temp2){
if(temp1<temp2){
sgtree[temp2]=sgtree[temp2]+data[l]-data[l+1];
temp2=(temp2-1)/2;
}
else{
sgtree[temp1]=sgtree[temp1]-data[l]+data[l+1];
temp1=(temp1-1)/2;
}
}
//updateSgTree(0,n-1,l,temp,0);
//updateSgTree(0,n-1,l+1,-temp,0);
/*
temp=data[l];
data[l]=data[l+1];
data[l+1]=temp;
*/
temp=data[l];
data[l]=data[l+1];
data[l+1]=temp;
}
l+=2;
}
}
}
return 0;
}
答案 0 :(得分:3)
每个查询的解决方案的时间复杂度至少为O(n)
(可能是O(n log n)
),因此显然太慢了。
以下是每个查询需要O(log n)
时间的解决方案:
使用隐式键构建treape:第一个将包含具有偶数索引的所有元素,第二个将包含具有奇数索引的所有元素。两个树都应该根据给定数组中的索引对元素进行排序。
求和查询非常简单:我们知道范围内的最小和最大奇数和偶数索引,因此我们可以在第一个和第二个树上运行此查询并返回总和。
可以通过以下方式处理交换查询:让我们将第一个树拆分为L1, M1, R1
个部分,将第二个树拆分为L2, M2, R2
(其中M1
M2
是查询范围内的部分。然后我们应该交换M1
和M2
并合并树,也就是说,第一棵树是合并L1, M2, R1
的结果,第二棵树是L2, M1, R2
。
每个查询都需要恒定数量的合并和拆分操作,因此总时间复杂度为O((n + m) * log n)
或O(n + m * log n)
(这取决于我们在回答查询之前构建这些treaps的方式)。