noi前第十二场 题解
A. string
对于这类字符串匹配题,有个套路是直接用 \(FFT\) 优化。
然后发现这题字符集很小,所以枚举一个字符,将匹配串中的这个字符设为 \(1\),模式串中的非这个字符设为 \(1\)。
然后用一些技巧优化一下,就可以用 \(|\sum|+1\) 次长度为 \(n\) 的 \(DFT\) 求出答案了。
B. Tree
容易发现这题就是通过与 \(LCT\) 类似的树构造出深度和最大的原树。
若一个节点在实链上,我们只关注每个深度的当前最大值。
若一个节点上面是虚边,我们只需要这个节点作为 \(splay\) 根节点的当前最大值。
所以可以设计一个 \(dp\), \(dp_{i,j}\) 表示以 \(i\) 为根节点,\(splay\) 大小为 \(j\) 的最大值。
\(f_i=\max \limits_{j=1}^{sz_i}dp_{i,j}\) 这个玩意表示若 \(i\) 为虚子树的最大值。
发现转移主要与左子树有关,因为左子树贡献了一些深度。
所以枚举左子树是谁,枚举左子树的大小就行了。
C. sort
如果只有排序操作,一个方法是这样的。
开一个平衡树维护已经排好序的每个连续段。
对于每个连续段,用 平衡树/\(01trie\) 等来实现分裂,暴力去合并复杂度就是对的。
拓展到本题上,仍然使用平衡树套 \(01trie\)。
在平衡树上打标记,表示对子树内所有 \(01trie\) 都进行这种操作。
在 \(01trie\) 上也打标记,表示对子树内节点进行这种操作。
可以认为,平衡树上的标记是上次排序到这次排序之间的,也就是说不影响相对关系的。
而 \(01trie\) 上的标记是上次排序之前的,也就是说影响相对关系。
所以当 \(01trie\) 上标记下传的时候,需要交换两个儿子或者合并两个儿子。
对于修改操作,只需要在区间的平衡树上打个标记。
对于排序操作,可以暴力拉出来每个连续段,把它上面的标记打在 \(01trie\) 上,并合并在一起。
思路大概就是这样,打起来有点恶心。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
int n,m;
int a[N];
struct Tag{
int A,B,C;
Tag():A(-1),B(0),C(0){}
inline friend void operator *= (Tag &a,const Tag &b){
a.A&=b.A; a.B&=b.A; a.C&=b.A;
a.B|=b.B; a.C&=-1^b.B; a.C^=b.C;
}
inline int mp(int k,int f){
return ((f&(A>>k&1))|(B>>k&1))^(C>>k&1);
}
};
namespace Trie{
int cnt;
int ch[N*180][2],sz[N*180];
Tag lzy[N*180];
inline void down(int p);
inline int merge(int x,int y,int dep);
inline void insert(int p,int x){
for(int i=31;~i;--i){
if(!ch[p][x>>i&1]) ch[p][x>>i&1]=++cnt;
++sz[p]; p=ch[p][x>>i&1];
} ++sz[p];
}
inline void update(int p){
sz[p]=sz[ch[p][0]]+sz[ch[p][1]];
}
inline void down(int p,int dep){
lzy[ch[p][0]]*=lzy[p]; lzy[ch[p][1]]*=lzy[p];
if(lzy[p].mp(dep-1,0)==1&&lzy[p].mp(dep-1,1)==0) swap(ch[p][0],ch[p][1]);
else if(lzy[p].mp(dep-1,0)==0&&lzy[p].mp(dep-1,1)==0) ch[p][0]=merge(ch[p][0],ch[p][1],dep-1),ch[p][1]=0;
else if(lzy[p].mp(dep-1,0)==1&&lzy[p].mp(dep-1,1)==1) ch[p][1]=merge(ch[p][0],ch[p][1],dep-1),ch[p][0]=0;
lzy[p]=Tag();
}
inline int merge(int x,int y,int dep){
if(!x||!y) return x|y;
sz[x]+=sz[y]; if(!dep) return x;
down(x,dep); down(y,dep);
ch[x][0]=merge(ch[x][0],ch[y][0],dep-1);
ch[x][1]=merge(ch[x][1],ch[y][1],dep-1);
return update(x),x;
}
inline void split(int p,int k,int dep,int &x,int &y){
if(!dep) return x=++cnt,y=++cnt,sz[x]=k,sz[y]=sz[p]-k,void();
down(p,dep); x=++cnt; y=++cnt;
if(sz[ch[p][0]]>=k) split(ch[p][0],k,dep-1,ch[x][0],ch[y][0]),ch[y][1]=ch[p][1],update(x),update(y);
else split(ch[p][1],k-sz[ch[p][0]],dep-1,ch[x][1],ch[y][1]),ch[x][0]=ch[p][0],update(x),update(y);
}
void dfs(int x,int dep,int ret,Tag now){
if(!dep){
int ans=0;
for(int i=0;i<32;++i) ans|=now.mp(i,ret>>i&1)<<i;
for(int i=0;i<sz[x];++i) printf("%u ",ans);
return ;
}
down(x,dep);
if(ch[x][0]) dfs(ch[x][0],dep-1,ret,now);
if(ch[x][1]) dfs(ch[x][1],dep-1,ret|(1<<dep-1),now);
}
}
namespace Treap{
int rt,cnt;
int ch[N*15][2],sz[N*15],rnd[N*15],l[N*15],r[N*15],tr[N*15];
Tag val[N*15],lzy[N*15];
inline void update(int p){
sz[p]=1+sz[ch[p][0]]+sz[ch[p][1]];
}
inline void down(int p){
val[ch[p][0]]*=lzy[p]; val[ch[p][1]]*=lzy[p];
lzy[ch[p][0]]*=lzy[p]; lzy[ch[p][1]]*=lzy[p];
lzy[p]=Tag();
}
inline void split(int p,int k,int &x,int &y){
if(!p) return x=y=0,void();
down(p);
if(sz[ch[p][0]]>=k) split(ch[p][0],k,x,ch[p][0]),y=p,update(p);
else split(ch[p][1],k-sz[ch[p][0]]-1,ch[p][1],y),x=p,update(p);
}
inline int merge(int x,int y){
if(!x||!y) return x|y;
if(rnd[x]>rnd[y]) return down(x),ch[x][1]=merge(ch[x][1],y),update(x),x;
else return down(y),ch[y][0]=merge(x,ch[y][0]),update(y),y;
}
inline int Rank(int p,int k,int ret=0){
while(p){
down(p);
if(r[p]<=k) ret+=sz[ch[p][0]]+1,p=ch[p][1];
else p=ch[p][0];
}
return ret;
}
inline void cut(int k){
int a,b;
split(rt,Rank(rt,k),rt,a);
if(!a) return ;
split(a,1,a,b);
if(l[a]==k+1) return rt=merge(rt,merge(a,b)),void();
int c=++cnt,d=++cnt,num=k-l[a]+1; val[c]=val[d]=val[a]; sz[c]=sz[d]=1;
rnd[c]=rand(); rnd[d]=rand();
Trie::split(tr[a],num,32,tr[c],tr[d]);
l[c]=l[a]; r[c]=k; l[d]=k+1; r[d]=r[a];
rt=merge(rt,merge(merge(c,d),b));
}
inline void Split(int L,int R,int &a,int &b,int &c){
cut(L-1); cut(R);
split(rt,Rank(rt,L-1),a,b);
split(b,Rank(b,R),b,c);
}
void dfs(int x,int h){
down(x);
Trie::lzy[tr[x]]*=val[x];
tr[h]=Trie::merge(tr[h],tr[x],32);
if(ch[x][0]) dfs(ch[x][0],h);
if(ch[x][1]) dfs(ch[x][1],h);
}
}
inline int read(register int x=0,register char ch=getchar()){
for(;!isdigit(ch);ch=getchar()) ;
for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return x;
}
int main(){
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
n=read(); m=read();
for(int i=1;i<=n;++i){
a[i]=read(); ++Treap::cnt;
Treap::l[i]=i; Treap::r[i]=i; Treap::sz[i]=1; Treap::rnd[i]=rand();
Treap::tr[i]=++Trie::cnt; Trie::insert(Treap::tr[i],a[i]);
Treap::rt=Treap::merge(Treap::rt,i);
}
for(int i=1,opt,l,r,x;i<=m;++i){
opt=read(); l=read(); r=read();
if(opt==1){
x=read(); int a,b,c; Tag now=Tag();
Treap::Split(l,r,a,b,c); now.A=-1; now.B=x; now.C=0;
Treap::lzy[b]*=now; Treap::val[b]*=now;
Treap::rt=Treap::merge(Treap::merge(a,b),c);
}
else if(opt==2){
x=read(); int a,b,c; Tag now=Tag();
Treap::Split(l,r,a,b,c); now.A=x; now.B=0; now.C=0;
Treap::lzy[b]*=now; Treap::val[b]*=now;
Treap::rt=Treap::merge(Treap::merge(a,b),c);
}
else if(opt==3){
x=read(); int a,b,c; Tag now=Tag();
Treap::Split(l,r,a,b,c); now.A=-1; now.B=0; now.C=x;
Treap::lzy[b]*=now; Treap::val[b]*=now;
Treap::rt=Treap::merge(Treap::merge(a,b),c);
}
else{
int a,b,c,d;
Treap::Split(l,r,a,b,c); d=++Treap::cnt; Treap::rnd[d]=rand();
Treap::sz[d]=1; Treap::l[d]=l; Treap::r[d]=r; Treap::dfs(b,d);
Treap::rt=Treap::merge(Treap::merge(a,d),c);
}
}
while(Treap::rt){
int x; Treap::split(Treap::rt,1,x,Treap::rt);
Trie::dfs(Treap::tr[x],32,0,Treap::val[x]);
}
return puts(""),0;
}