题解 P3391【【模板】文艺平衡树】
违规用户名76G!ihcm · · 题解
大家好,我非常喜欢暴力数据结构,于是我就用分块过了这道题。
而且比大多数平衡树跑得快。
这个题对序列分块肯定是不可取的。
块状链表又难写又慢。
所以考虑对时间分块,根号重构。(虽然块状链表好像类似,但是我的做法好写得多)
考虑原序列经过几次翻转之后得到的序列,是由原序列的一些子区间,正序或者逆序拼成的,比如:
原序列:
翻转
翻转
在原序列上的子区间是
而且有一个非常好的性质就是每次操作最多增加常数个子区间。
因为对于上个状态的某个子区间,如果它和这次翻转的区间没有交,那么它不会动。
如果它被这次翻转的区间包含,那么它会被翻转,并且换个位置,数量不会增加。
如果它与这次翻转的相交,那么它会最多裂开成
一个区间最多与
回到这个题上,考虑维护每个子区间,而不是原序列。
每次修改暴力枚举所有子区间,看一下它们会变成什么,这个的复杂度是
但是每次操作后都有可能增加几个子区间,到后面每次操作就趋近
也就是说,一旦当前区间个数超过了某个阈值
比如:
翻转
翻转
翻转
这时候觉得区间有点多,重构一下。
注意这个时候原序列已经没关系了,只用把当前序列表示成上一次重构时的一些子区间,正序或逆序拼成的。
翻转
翻转
这个做法的时间复杂度是
取
具体实现的时候需要记一下每个子区间在当前序列的位置,以及它们在上一次重构时的位置,还有是否被取反。
分裂子区间的时候就直接加到后面。
重构的时候直接把所有区间都删了,换成
代码(还挺好写的):
#include <iostream>
using namespace std;
const int bl=400;//阈值大小
int a[100005],b[100005],cnt,n,q;//cnt表示子区间个数
struct node{//子区间
int l1,l2,r2,f;//l1是原序列上左端点的位置,
//l2,r2是现序列上的位置,f是是否翻转,由于r1-l1一定=r2-l2,所以r1就不记了
}s[405];
inline void rev(int l,int r){//翻转区间 [l,r]
for(int i=1;i<=cnt;i++){//遍历所有子区间
if(s[i].r2<l||s[i].l2>r) continue;//没有交直接跳过
if(l>s[i].l2){//右边相交,要分裂
if(!s[i].f) s[++cnt]={l-s[i].l2+s[i].l1,l,s[i].r2,0},s[i]={s[i].l1,s[i].l2,l-1,0};
else s[++cnt]={s[i].l1,l,s[i].r2,1},s[i]={s[i].l1+s[i].r2-l+1,s[i].l2,l-1,1};
//计算分裂完的两个区间的信息
}
if(r<s[i].r2){//左边相交,同理,代码上只是把l换成了r+1
if(!s[i].f) s[++cnt]={r+1-s[i].l2+s[i].l1,r+1,s[i].r2,0},s[i]={s[i].l1,s[i].l2,r+1-1,0};
else s[++cnt]={s[i].l1,r+1,s[i].r2,1},s[i]={s[i].l1+s[i].r2-r,s[i].l2,r,1};
}
if(s[i].r2<l||s[i].l2>r) continue;//分裂完不相交
s[i].l2=r-s[i].l2+l,s[i].r2=r-s[i].r2+l,swap(s[i].l2,s[i].r2),s[i].f^=1;
//计算翻转后区间的信息
}
}
inline void qwq(){//重构
for(int i=1;i<=cnt;i++){//把每个区间的值暴力赋过去
if(s[i].f) for(int j=s[i].r2;j>=s[i].l2;j--) b[j]=a[s[i].l1++];
else for(int j=s[i].l2;j<=s[i].r2;j++) b[j]=a[s[i].l1++];
}
cnt=1,s[cnt]={1,1,n,0},swap(a,b);//删掉原来所有区间
}
int main(int argc, char** argv) {
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) a[i]=i;
s[++cnt]={1,1,n,0};//一开始只有整个一个区间
while(q--){
int l,r;
scanf("%d%d",&l,&r),rev(l,r);
if(cnt>bl) qwq();//如果区间数大于阈值,就重构
}
qwq();//算最终的答案,相当于一次重构
for(int i=1;i<=n;i++) printf("%d ",a[i]);
return 0;
}
有评论的话我可能半年之内回不了,因为被禁言了/cy
有什么 bug 或者假了或者被踩了私信联系我
求过审