题目描述
长度为
n
n
n 的序列,
0
≤
a
i
<
2
32
0\le a_i<2^{32}
0≤ai<232
要求支持 区间与,区间或,区间异或,区间排序。
输出最终的
a
a
a 序列。
n
,
m
≤
1
0
5
n,m\le10^5
n,m≤105
题目分析
a i < 16 a_i<16 ai<16 的时候可以线段树维护每种数的个数,排序的时候提取每种数然后区间赋值。看似是 l o g 3 log^3 log3 实际上却跑得很快,(可能有些数没有;赋值时的pushup可以小常数memset)
正解:
平衡树维护序列,01trie 维护已经排好序的权值递增段。
排序时用平衡树找到这段区间,在边角需要 trie 分裂
然后将这段区间对应的所有 trie 合并。合并前先下放标记,保证有序。
标记可能是把某些位强制变0/1,翻转某些位,对应的操作是左右儿子跑到一边/交换左右儿子,前者做一次 trie 合并。(下放标记和合并嵌套了诶!)
将合并完的 trie 加入平衡树。
分裂 trie 时平衡树的标记不打到 trie 上,因为在序列中的位置没有变,但是 trie 内部的标记要下放,是排序没排完的。
修改同样分裂出这段区间,然后在平衡树上打标记。在排序时把平衡树上的标记打到 trie 上,修改时不打在 trie 上。
平衡树点上只需要存懒标记、缓存的准备打在trie上的标记、 trie 的根、子树大小。
trie 只需要存懒标记,子树大小。
这个标记下放有点难搞。。。我存了3个标记,强制赋0的位,强制赋1的位,翻转的位,二进制表示。
先用0/1标记强制修改,再看翻转标记对应儿子的强制修改标记,剩下的翻转位与儿子的翻转标记异或。
trie 合并的复杂度是 O ( 删 除 节 点 个 数 ) O(删除节点个数) O(删除节点个数) ,每次分裂增加 O ( log ) O(\log) O(log) 个点。所以总复杂度还是 O ( ( n + m ) ( log V + log n ) ) O((n+m)(\log V+\log n)) O((n+m)(logV+logn)) 的
OI生涯第二长的代码。。调出无数锅。。
- 注意 trie 树分裂和输出答案的时候到最底层可能不止一个点。
- Treap分裂的时候注意标记也要复制。
- 新建Treap节点的时候 siz 是对应 trie 树的大小而不是 1
- 最后输出答案的时候平衡树上的标记不是打到 trie 上,那样就是排序了,应该对最底层的值施加这个标记。
- trie 分裂近乎稳稳的 32 32 32 层,如果不写空间回收的话数组要开到 3*log(初始一个log,Treap分裂两次)
Code:
#include<bits/stdc++.h>
#define maxn 100005
#define maxp maxn*105
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
template<class T>void read(T &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
typedef unsigned int ui;
int n,m;
ui a[maxn];
struct tag{
ui x[2],rev;
tag(ui a=0,ui b=0,ui c=0){x[0]=a,x[1]=b,rev=c;}
bool empty(){return !x[0]&&!x[1]&&!rev;}
void operator += (const tag &t){
for(int i=0;i<2;i++){
ui v=t.x[i];
x[i]|=v, x[!i]&=~v, rev&=~v;
}
ui v=(x[0]&t.rev)|(x[1]&t.rev);
x[0]^=v,x[1]^=v;
rev^=t.rev^v;
}
void init(ui &v){
v&=~x[0], v|=x[1], v^=rev;
}
}treap[maxn],trie[maxp],tg[maxp];
int ch[maxp][2],cnt[maxp],tot;
void ins(int &rt,ui x){
if(!rt) rt=++tot;
int r=rt,v;
for(int i=31;i>=0;r=ch[r][v],i--){
cnt[r]++;
if(!ch[r][v=x>>i&1]) ch[r][v]=++tot;
}
cnt[r]++;
}
void merge_trie(int&,int,int,int);
void down_trie(int i,int d){
if(d<0||tg[i].empty()) return;
if(tg[i].x[0]>>d&1) merge_trie(ch[i][0],ch[i][0],ch[i][1],d-1),ch[i][1]=0;
else if(tg[i].x[1]>>d&1) merge_trie(ch[i][1],ch[i][1],ch[i][0],d-1),ch[i][0]=0;
else if(tg[i].rev>>d&1) swap(ch[i][0],ch[i][1]);
if(ch[i][0]) tg[ch[i][0]]+=tg[i];
if(ch[i][1]) tg[ch[i][1]]+=tg[i];
tg[i]=tag();
}
void merge_trie(int &t,int x,int y,int d){
if(!x||!y) return void(t=x+y);
down_trie(x,d),down_trie(y,d);
cnt[t=x]+=cnt[y];
merge_trie(ch[t][0],ch[x][0],ch[y][0],d-1),merge_trie(ch[t][1],ch[x][1],ch[y][1],d-1);
}
void split_trie(int x,int &y,int k,int d){
y=++tot;
if(d==-1) {cnt[y]=cnt[x]-k,cnt[x]=k; return;}
down_trie(x,d); int sz=cnt[ch[x][0]];
if(k>sz) split_trie(ch[x][1],ch[y][1],k-sz,d-1);
else swap(ch[x][1],ch[y][1]);
if(k<sz) split_trie(ch[x][0],ch[y][0],k,d-1);
cnt[y]=cnt[x]-k,cnt[x]=k;
}
int root,lc[maxn],rc[maxn],siz[maxn],rnd[maxn],rt[maxn],rec[maxn],tmprt;
int Newtreap(int r){
int i=rec[(*rec)--];
return siz[i]=cnt[r],rnd[i]=rand(),rt[i]=r,i;//attention lc,rc,tag.
}
void upd_treap(int i){siz[i]=siz[lc[i]]+siz[rc[i]]+cnt[rt[i]];}
void down_treap(int i){
if(treap[i].empty()) return;
if(lc[i]) treap[lc[i]]+=treap[i],trie[lc[i]]+=treap[i];
if(rc[i]) treap[rc[i]]+=treap[i],trie[rc[i]]+=treap[i];
treap[i]=tag();
}
void merge(int &t,int a,int b){
if(!a||!b) return void(t=a+b);
if(rnd[a]<rnd[b]) down_treap(t=a),merge(rc[t],rc[a],b);
else down_treap(t=b),merge(lc[t],a,lc[b]);
upd_treap(t);
}
void split(int t,int &a,int &b,int k){
if(!t) return void(a=b=0);
down_treap(t);
if(k<=siz[lc[t]]) b=t,split(lc[t],a,lc[b],k);
else if(k>=siz[lc[t]]+cnt[rt[t]]) a=t,split(rc[t],rc[a],b,k-siz[lc[t]]-cnt[rt[t]]);
else{
split_trie(rt[t],tmprt,k-siz[lc[t]],31);
a=t, b=Newtreap(tmprt),rc[b]=rc[t],rc[t]=0,upd_treap(b);
trie[b]=trie[t]; //!!!
}
upd_treap(t);
}
void dfs(int &i){
if(!i) return;
down_treap(i),tg[rt[i]]+=trie[i],trie[i]=tag();
merge_trie(tmprt,tmprt,rt[i],31),rt[i]=0;
dfs(lc[i]),dfs(rc[i]),rec[++*rec]=i,i=0;
}
ui ans[maxn];
void final(int i,ui x,int d,tag offset){
if(d==-1) {offset.init(x); while(cnt[i]--) ans[++*ans]=x; return;}
down_trie(i,d);
if(ch[i][0]) final(ch[i][0],x,d-1,offset);//cnt[ch[i][0]]?
if(ch[i][1]) final(ch[i][1],x|1u<<d,d-1,offset);
}
void final(int i){
if(!i) return;
down_treap(i);
final(lc[i]),final(rt[i],0,31,trie[i]),final(rc[i]);
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
read(n),read(m);
for(int i=1;i<=n;i++) rec[++*rec]=n-i+1;
for(int i=1,x;i<=n;i++) read(x),ins(tmprt=0,x),merge(root,root,Newtreap(tmprt));
ui v;
for(int op,l,r,L,M,R;m--;){
read(op),read(l),read(r),op!=4&&(read(v),0);
split(root,L,R,r),split(L,L,M,l-1);
if(op<=3){
if(op==1) treap[M]+=tag(0,v,0),trie[M]+=tag(0,v,0);
if(op==2) treap[M]+=tag(~v,0,0),trie[M]+=tag(~v,0,0);
if(op==3) treap[M]+=tag(0,0,v),trie[M]+=tag(0,0,v);
merge(L,L,M),merge(root,L,R);
}
if(op==4){
tmprt=0,dfs(M);
merge(L,L,Newtreap(tmprt)),merge(root,L,R);
}
//cerr<<tot<<endl;
}
final(root);
for(int i=1;i<=n;i++) printf("%u%c",ans[i],i==n?10:32);
}